123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555 |
- #if !BESTHTTP_DISABLE_SOCKETIO
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using BestHTTP.PlatformSupport.Memory;
- using BestHTTP.SocketIO3.Events;
- namespace BestHTTP.SocketIO3.Parsers
- {
- public sealed class Placeholder
- {
- public bool _placeholder;
- public int num;
- }
- [PlatformSupport.IL2CPP.Il2CppEagerStaticClassConstructionAttribute]
- public sealed class DefaultJsonParser : IParser
- {
- static DefaultJsonParser()
- {
- BestHTTP.JSON.LitJson.JsonMapper.RegisterImporter<string, byte[]>(str => Convert.FromBase64String(str));
- }
- private IncomingPacket PacketWithAttachment = IncomingPacket.Empty;
- private int ToInt(char ch)
- {
- int charValue = Convert.ToInt32(ch);
- int num = charValue - '0';
- if (num < 0 || num > 9)
- return -1;
- return num;
- }
- public IncomingPacket Parse(SocketManager manager, string from)
- {
- int idx = 0;
- var transportEvent = (TransportEventTypes)ToInt(from[idx++]);
- var socketIOEvent = SocketIOEventTypes.Unknown;
- var nsp = string.Empty;
- var id = -1;
- var payload = string.Empty;
- int attachments = 0;
- if (from.Length > idx && ToInt(from[idx]) >= 0)
- socketIOEvent = (SocketIOEventTypes)ToInt(from[idx++]);
- else
- socketIOEvent = SocketIOEventTypes.Unknown;
- // Parse Attachment
- if (socketIOEvent == SocketIOEventTypes.BinaryEvent || socketIOEvent == SocketIOEventTypes.BinaryAck)
- {
- int endIdx = from.IndexOf('-', idx);
- if (endIdx == -1)
- endIdx = from.Length;
- int.TryParse(from.Substring(idx, endIdx - idx), out attachments);
- idx = endIdx + 1;
- }
- // Parse Namespace
- if (from.Length > idx && from[idx] == '/')
- {
- int endIdx = from.IndexOf(',', idx);
- if (endIdx == -1)
- endIdx = from.Length;
- nsp = from.Substring(idx, endIdx - idx);
- idx = endIdx + 1;
- }
- else
- nsp = "/";
- // Parse Id
- if (from.Length > idx && ToInt(from[idx]) >= 0)
- {
- int startIdx = idx++;
- while (from.Length > idx && ToInt(from[idx]) >= 0)
- idx++;
- int.TryParse(from.Substring(startIdx, idx - startIdx), out id);
- }
- // What left is the payload data
- if (from.Length > idx)
- payload = from.Substring(idx);
- else
- payload = string.Empty;
- var packet = new IncomingPacket(transportEvent, socketIOEvent, nsp, id);
- packet.AttachementCount = attachments;
- string eventName = packet.EventName;
- object[] args = null;
- switch (socketIOEvent)
- {
- case SocketIOEventTypes.Unknown:
- packet.DecodedArg = payload;
- break;
- case SocketIOEventTypes.Connect:
- // No Data | Object
- if (!string.IsNullOrEmpty(payload))
- (eventName, args) = ReadData(manager, packet, payload);
- break;
- case SocketIOEventTypes.Disconnect:
- // No Data
- break;
- case SocketIOEventTypes.Error:
- // String | Object
- (eventName, args) = ReadData(manager, packet, payload);
- break;
- default:
- // Array
- (eventName, args) = ReadData(manager, packet, payload);
- // Save payload until all attachments arrive
- if (packet.AttachementCount > 0)
- packet.DecodedArg = payload;
- break;
- }
- packet.EventName = eventName;
-
- if (args != null)
- {
- if (args.Length == 1)
- packet.DecodedArg = args[0];
- else
- packet.DecodedArgs = args;
- }
- if (packet.AttachementCount > 0)
- {
- PacketWithAttachment = packet;
- return IncomingPacket.Empty;
- }
- return packet;
- }
- public IncomingPacket MergeAttachements(SocketManager manager, IncomingPacket packet)
- {
- string payload = packet.DecodedArg as string;
- packet.DecodedArg = null;
- string placeholderFormat = "{{\"_placeholder\":true,\"num\":{0}}}";
- for (int i = 0; i < packet.Attachements.Count; ++i)
- {
- string placeholder = string.Format(placeholderFormat, i);
- BufferSegment data = packet.Attachements[i];
- payload = payload.Replace(placeholder, "\"" + Convert.ToBase64String(data.Data, data.Offset, data.Count) + "\"");
- }
- (string eventName, object[] args) = ReadData(manager, packet, payload);
- packet.EventName = eventName;
-
- if (args != null)
- {
- if (args.Length == 1)
- packet.DecodedArg = args[0];
- else
- packet.DecodedArgs = args;
- }
- return packet;
- }
- private (string, object[]) ReadData(SocketManager manager, IncomingPacket packet, string payload)
- {
- Socket socket = manager.GetSocket(packet.Namespace);
- string eventName = packet.EventName;
- Subscription subscription = socket.GetSubscription(eventName);
- object[] args = null;
- switch (packet.SocketIOEvent)
- {
- case SocketIOEventTypes.Unknown:
- // TODO: Error?
- break;
- case SocketIOEventTypes.Connect:
- // No Data | Object
- using (var strReader = new System.IO.StringReader(payload))
- args = ReadParameters(socket, subscription, strReader);
- break;
- case SocketIOEventTypes.Disconnect:
- // No Data
- break;
- case SocketIOEventTypes.Error:
- // String | Object
- switch (payload[0])
- {
- case '{':
- using (var strReader = new System.IO.StringReader(payload))
- args = ReadParameters(socket, subscription, strReader);
- break;
- default:
- args = new object[] { new Error(payload) };
- break;
- }
- break;
- case SocketIOEventTypes.Ack:
- eventName = IncomingPacket.GenerateAcknowledgementNameFromId(packet.Id);
- subscription = socket.GetSubscription(eventName);
- args = ReadParameters(socket, subscription, JSON.LitJson.JsonMapper.ToObject<List<object>>(payload), 0);
-
- break;
- default:
- // Array
- List<object> array = null;
- using (var reader = new System.IO.StringReader(payload))
- array = JSON.LitJson.JsonMapper.ToObject<List<object>>(new JSON.LitJson.JsonReader(reader));
- if (array.Count > 0)
- {
- eventName = array[0].ToString();
- subscription = socket.GetSubscription(eventName);
- }
- if (packet.AttachementCount == 0 || packet.Attachements != null)
- {
- try
- {
- args = ReadParameters(socket, subscription, array, 1);
- }
- catch(Exception ex)
- {
- HTTPManager.Logger.Exception("DefaultJsonParser", string.Format("ReadParameters with eventName: {0}", eventName), ex);
- }
- }
- break;
- }
- return (eventName, args);
- }
- private object[] ReadParameters(Socket socket, Subscription subscription, List<object> array, int startIdx)
- {
- object[] args = null;
- if (array.Count > startIdx)
- {
- var desc = subscription != null ? subscription.callbacks.FirstOrDefault() : default(CallbackDescriptor);
- int paramCount = desc.ParamTypes != null ? desc.ParamTypes.Length : 0;
- int arrayIdx = startIdx;
- if (paramCount > 0)
- {
- args = new object[paramCount];
- for (int i = 0; i < desc.ParamTypes.Length; ++i)
- {
- Type type = desc.ParamTypes[i];
- if (type == typeof(Socket))
- args[i] = socket;
- else if (type == typeof(SocketManager))
- args[i] = socket.Manager;
- else if (type == typeof(Placeholder))
- args[i] = new Placeholder();
- else
- args[i] = ConvertTo(desc.ParamTypes[i], array[arrayIdx++]);
- }
- }
- }
- return args;
- }
- public object ConvertTo(Type toType, object obj)
- {
- if (obj == null)
- return null;
- #if NETFX_CORE
- TypeInfo objType = obj.GetType().GetTypeInfo();
- #else
- Type objType = obj.GetType();
- #endif
- #if NETFX_CORE
- TypeInfo typeInfo = toType.GetTypeInfo();
- #endif
- #if NETFX_CORE
- if (typeInfo.IsEnum)
- #else
- if (toType.IsEnum)
- #endif
- return Enum.Parse(toType, obj.ToString(), true);
- #if NETFX_CORE
- if (typeInfo.IsPrimitive)
- #else
- if (toType.IsPrimitive)
- #endif
- return Convert.ChangeType(obj, toType);
- if (toType == typeof(string))
- return obj.ToString();
- #if NETFX_CORE
- if (typeInfo.IsGenericType && toType.Name == "Nullable`1")
- return Convert.ChangeType(obj, toType.GenericTypeArguments[0]);
- #else
- if (toType.IsGenericType && toType.Name == "Nullable`1")
- return Convert.ChangeType(obj, toType.GetGenericArguments()[0]);
- #endif
- #if NETFX_CORE
- if (objType.Equals(typeInfo))
- #else
- if (objType.Equals(toType))
- #endif
- return obj;
- if (toType == typeof(byte[]) && objType == typeof(string))
- return Convert.FromBase64String(obj.ToString());
- return JSON.LitJson.JsonMapper.ToObject(toType, JSON.LitJson.JsonMapper.ToJson(obj));
- }
- private object[] ReadParameters(Socket socket, Subscription subscription, System.IO.TextReader reader)
- {
- var desc = subscription != null ? subscription.callbacks.FirstOrDefault() : default(CallbackDescriptor);
- int paramCount = desc.ParamTypes != null ? desc.ParamTypes.Length : 0;
- object[] args = null;
- if (paramCount > 0)
- {
- args = new object[paramCount];
- for (int i = 0; i < desc.ParamTypes.Length; ++i)
- {
- Type type = desc.ParamTypes[i];
- if (type == typeof(Socket))
- args[i] = socket;
- else if (type == typeof(SocketManager))
- args[i] = socket.Manager;
- else {
- BestHTTP.JSON.LitJson.JsonReader jr = new JSON.LitJson.JsonReader(reader);
- args[i] = JSON.LitJson.JsonMapper.ToObject(desc.ParamTypes[i], jr);
- reader.Read();
- }
- }
- }
- return args;
- }
- public IncomingPacket Parse(SocketManager manager, BufferSegment data, TransportEventTypes transportEvent = TransportEventTypes.Unknown)
- {
- IncomingPacket packet = IncomingPacket.Empty;
- if (PacketWithAttachment.Attachements == null)
- PacketWithAttachment.Attachements = new List<BufferSegment>(PacketWithAttachment.AttachementCount);
- PacketWithAttachment.Attachements.Add(data);
-
- if (PacketWithAttachment.Attachements.Count == PacketWithAttachment.AttachementCount)
- {
- packet = manager.Parser.MergeAttachements(manager, PacketWithAttachment);
- PacketWithAttachment = IncomingPacket.Empty;
- }
- return packet;
- }
- public OutgoingPacket CreateOutgoing(TransportEventTypes transportEvent, string payload)
- {
- return new OutgoingPacket { Payload = "" + (char)('0' + (byte)transportEvent) + payload };
- }
- private StringBuilder builder = new StringBuilder();
- public OutgoingPacket CreateOutgoing(Socket socket, SocketIOEventTypes socketIOEvent, int id, string name, object arg)
- {
- return CreateOutgoing(socket, socketIOEvent, id, name, arg != null ? new object[] { arg } : null);
- }
- private int GetBinaryCount(object[] args)
- {
- if (args == null || args.Length == 0)
- return 0;
- int count = 0;
- for (int i = 0; i < args.Length; ++i)
- if (args[i] is byte[])
- count++;
- return count;
- }
- public OutgoingPacket CreateOutgoing(Socket socket, SocketIOEventTypes socketIOEvent, int id, string name, object[] args)
- {
- builder.Length = 0;
- List<byte[]> attachements = null;
- switch(socketIOEvent)
- {
- case SocketIOEventTypes.Ack:
- if (GetBinaryCount(args) > 0)
- {
- attachements = CreatePlaceholders(args);
- socketIOEvent = SocketIOEventTypes.BinaryAck;
- }
- break;
- case SocketIOEventTypes.Event:
- if (GetBinaryCount(args) > 0)
- {
- attachements = CreatePlaceholders(args);
- socketIOEvent = SocketIOEventTypes.BinaryEvent;
- }
- break;
- }
- builder.Append(((int)TransportEventTypes.Message).ToString());
- builder.Append(((int)socketIOEvent).ToString());
- if (socketIOEvent == SocketIOEventTypes.BinaryEvent || socketIOEvent == SocketIOEventTypes.BinaryAck)
- {
- builder.Append(attachements.Count.ToString());
- builder.Append('-');
- }
- // Add the namespace. If there is any other then the root nsp ("/")
- // then we have to add a trailing "," if we have more data.
- bool nspAdded = false;
- if (socket.Namespace != "/")
- {
- builder.Append(socket.Namespace);
- nspAdded = true;
- }
- // ack id, if any
- if (id >= 0)
- {
- if (nspAdded)
- {
- builder.Append(',');
- nspAdded = false;
- }
- builder.Append(id.ToString());
- }
- // payload
- switch (socketIOEvent)
- {
- case SocketIOEventTypes.Connect:
- // No Data | Object
- if (args != null && args.Length > 0)
- {
- if (nspAdded) builder.Append(',');
- builder.Append(BestHTTP.JSON.LitJson.JsonMapper.ToJson(args[0]));
- }
- break;
- case SocketIOEventTypes.Disconnect:
- // No Data
- break;
- case SocketIOEventTypes.Error:
- // String | Object
- if (args != null && args.Length > 0)
- {
- if (nspAdded) builder.Append(',');
- builder.Append(BestHTTP.JSON.LitJson.JsonMapper.ToJson(args[0]));
- }
- break;
- case SocketIOEventTypes.Ack:
- case SocketIOEventTypes.BinaryAck:
- if (nspAdded) builder.Append(',');
- if (args != null && args.Length > 0)
- {
- var argsJson = JSON.LitJson.JsonMapper.ToJson(args);
- builder.Append(argsJson);
- }
- else
- builder.Append("[]");
- break;
- default:
- if (nspAdded) builder.Append(',');
- // Array
- builder.Append('[');
- if (!string.IsNullOrEmpty(name))
- {
- builder.Append('\"');
- builder.Append(name);
- builder.Append('\"');
- }
- if (args != null && args.Length > 0)
- {
- builder.Append(',');
- var argsJson = JSON.LitJson.JsonMapper.ToJson(args);
- builder.Append(argsJson, 1, argsJson.Length - 2);
- }
- builder.Append(']');
- break;
- }
- return new OutgoingPacket { Payload = builder.ToString(), Attachements = attachements };
- }
- private List<byte[]> CreatePlaceholders(object[] args)
- {
- List<byte[]> attachements = null;
- for (int i = 0; i < args.Length; ++i)
- {
- var binary = args[i] as byte[];
- if (binary != null)
- {
- if (attachements == null)
- attachements = new List<byte[]>();
- attachements.Add(binary);
- args[i] = new Placeholder { _placeholder = true, num = attachements.Count - 1 };
- }
- }
- return attachements;
- }
- }
- }
- #endif
|