You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
484 lines
17 KiB
484 lines
17 KiB
#if !BESTHTTP_DISABLE_SOCKETIO && BESTHTTP_SOCKETIO_ENABLE_GAMEDEVWARE_MESSAGEPACK |
|
|
|
using System; |
|
using System.Linq; |
|
|
|
using BestHTTP.PlatformSupport.Memory; |
|
using BestHTTP.SocketIO3.Events; |
|
|
|
using GameDevWare.Serialization; |
|
using GameDevWare.Serialization.MessagePack; |
|
using GameDevWare.Serialization.Serializers; |
|
|
|
namespace BestHTTP.SocketIO3.Parsers |
|
{ |
|
public sealed class MsgPackParser : IParser |
|
{ |
|
/// <summary> |
|
/// Custom function instead of char.GetNumericValue, as it throws an error under WebGL using the new 4.x runtime. |
|
/// It will return the value of the char if it's a numeric one, otherwise -1. |
|
/// </summary> |
|
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 MergeAttachements(SocketManager manager, IncomingPacket packet) { return packet; } |
|
|
|
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; |
|
|
|
return new IncomingPacket(transportEvent, socketIOEvent, nsp, id) { DecodedArg = payload, AttachementCount = attachments }; |
|
} |
|
|
|
public IncomingPacket Parse(SocketManager manager, BufferSegment data, TransportEventTypes transportEvent = TransportEventTypes.Unknown) |
|
{ |
|
using (var stream = new System.IO.MemoryStream(data.Data, data.Offset, data.Count)) |
|
{ |
|
var buff = BufferPool.Get(MsgPackReader.DEFAULT_BUFFER_SIZE, true); |
|
try |
|
{ |
|
var context = new SerializationContext |
|
{ |
|
Options = SerializationOptions.SuppressTypeInformation/*, |
|
ExtensionTypeHandler = CustomMessagePackExtensionTypeHandler.Instance*/ |
|
}; |
|
IJsonReader reader = new MsgPackReader(stream, context, Endianness.BigEndian, buff); |
|
|
|
reader.ReadObjectBegin(); |
|
|
|
int type = -1, id = -1; |
|
string nsp = null; |
|
|
|
bool hasData = false, readData = false; |
|
|
|
IncomingPacket packet = IncomingPacket.Empty; |
|
|
|
READ: |
|
|
|
while (reader.Token != JsonToken.EndOfObject) |
|
{ |
|
string key = reader.ReadMember(); |
|
|
|
switch(key) |
|
{ |
|
case "type": |
|
type = reader.ReadByte(); |
|
break; |
|
|
|
case "nsp": |
|
nsp = reader.ReadString(); |
|
break; |
|
|
|
case "id": |
|
id = reader.ReadInt32(); |
|
break; |
|
|
|
case "data": |
|
if (!hasData) |
|
{ |
|
hasData = true; |
|
SkipData(reader, (SocketIOEventTypes)type); |
|
} |
|
else |
|
{ |
|
readData = true; |
|
|
|
packet = new IncomingPacket(transportEvent != TransportEventTypes.Unknown ? transportEvent : TransportEventTypes.Message, (SocketIOEventTypes)type, nsp, id); |
|
(string eventName, object[] args) = ReadData(manager, packet, reader); |
|
|
|
packet.EventName = eventName; |
|
if (args != null) |
|
{ |
|
if (args.Length == 1) |
|
packet.DecodedArg = args[0]; |
|
else |
|
packet.DecodedArgs = args; |
|
} |
|
} |
|
break; |
|
} |
|
} |
|
|
|
// type, nsp, id and data can come in any order. To read data strongly typed we need to know all the additional fields before processing the data field. |
|
// In order to do it, when we first encounter the data field we skip it than we do a reset and an additional turn but reading the data too now. |
|
if (hasData && !readData) |
|
{ |
|
reader.Reset(); |
|
stream.Position = 0; |
|
reader.ReadObjectBegin(); |
|
|
|
goto READ; |
|
} |
|
|
|
reader.ReadObjectEnd(); |
|
|
|
return packet.Equals(IncomingPacket.Empty) ? new IncomingPacket(transportEvent != TransportEventTypes.Unknown ? transportEvent : TransportEventTypes.Message, (SocketIOEventTypes)type, nsp, id) : packet; |
|
} |
|
finally |
|
{ |
|
BufferPool.Release(buff); |
|
} |
|
} |
|
} |
|
|
|
private void SkipData(IJsonReader reader, SocketIOEventTypes type) |
|
{ |
|
switch (type) |
|
{ |
|
case SocketIOEventTypes.Unknown: |
|
// TODO: Error? |
|
break; |
|
|
|
case SocketIOEventTypes.Connect: |
|
// No Data | Object |
|
SkipObject(reader); |
|
break; |
|
|
|
case SocketIOEventTypes.Disconnect: |
|
// No Data |
|
break; |
|
|
|
case SocketIOEventTypes.Error: |
|
// String | Object |
|
switch(reader.Token) |
|
{ |
|
case JsonToken.StringLiteral: |
|
reader.ReadString(); |
|
break; |
|
|
|
case JsonToken.BeginObject: |
|
SkipObject(reader); |
|
break; |
|
} |
|
break; |
|
|
|
default: |
|
// Array |
|
SkipArray(reader, false, true); |
|
break; |
|
} |
|
} |
|
|
|
private object[] ReadParameters(Socket socket, Subscription subscription, IJsonReader reader, bool isInArray) |
|
{ |
|
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 |
|
args[i] = reader.ReadValue(desc.ParamTypes[i]); |
|
} |
|
} |
|
else |
|
{ |
|
if (isInArray) |
|
{ |
|
if (reader.Token != JsonToken.EndOfArray) |
|
SkipArray(reader, true, false); |
|
} |
|
else |
|
{ |
|
SkipObject(reader); |
|
} |
|
} |
|
|
|
return args; |
|
} |
|
|
|
private (string, object[]) ReadData(SocketManager manager, IncomingPacket packet, IJsonReader reader) |
|
{ |
|
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 |
|
args = ReadParameters(socket, subscription, reader, false); |
|
//SkipObject(reader); |
|
break; |
|
|
|
case SocketIOEventTypes.Disconnect: |
|
// No Data |
|
break; |
|
|
|
case SocketIOEventTypes.Error: |
|
// String | Object |
|
switch(reader.Token) |
|
{ |
|
case JsonToken.StringLiteral: |
|
args = new object[] { new Error(reader.ReadString()) }; |
|
break; |
|
|
|
case JsonToken.BeginObject: |
|
args = ReadParameters(socket, subscription, reader, false); |
|
//if (subscription == null && args == null) |
|
// SkipObject(reader); |
|
break; |
|
} |
|
break; |
|
|
|
case SocketIOEventTypes.Ack: |
|
eventName = IncomingPacket.GenerateAcknowledgementNameFromId(packet.Id); |
|
subscription = socket.GetSubscription(eventName); |
|
|
|
reader.ReadArrayBegin(); |
|
|
|
args = ReadParameters(socket, subscription, reader, true); |
|
|
|
reader.ReadArrayEnd(); |
|
break; |
|
|
|
default: |
|
// Array |
|
reader.ReadArrayBegin(); |
|
eventName = reader.ReadString(); |
|
|
|
subscription = socket.GetSubscription(eventName); |
|
|
|
args = ReadParameters(socket, subscription, reader, true); |
|
|
|
reader.ReadArrayEnd(); |
|
break; |
|
} |
|
|
|
return (eventName, args); |
|
} |
|
|
|
private void SkipArray(IJsonReader reader, bool alreadyStarted, bool readFinalArrayToken) |
|
{ |
|
if (!alreadyStarted) |
|
reader.ReadArrayBegin(); |
|
|
|
int arrayBegins = 1; |
|
|
|
while (arrayBegins > 0) |
|
{ |
|
switch (reader.Token) |
|
{ |
|
case JsonToken.BeginArray: arrayBegins++; break; |
|
case JsonToken.EndOfArray: arrayBegins--; break; |
|
} |
|
|
|
if (readFinalArrayToken || arrayBegins >= 1) |
|
reader.NextToken(); |
|
} |
|
} |
|
|
|
private void SkipObject(IJsonReader reader) |
|
{ |
|
reader.ReadObjectBegin(); |
|
int objectBegins = 1; |
|
|
|
while (objectBegins > 0) |
|
{ |
|
switch (reader.Token) |
|
{ |
|
case JsonToken.BeginObject: objectBegins++; break; |
|
case JsonToken.EndOfObject: objectBegins--; break; |
|
} |
|
reader.NextToken(); |
|
} |
|
} |
|
|
|
public OutgoingPacket CreateOutgoing(TransportEventTypes transportEvent, string payload) |
|
{ |
|
return new OutgoingPacket { Payload = "" + (char)('0' + (byte)transportEvent) + payload }; |
|
} |
|
|
|
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); |
|
} |
|
|
|
public OutgoingPacket CreateOutgoing(Socket socket, SocketIOEventTypes socketIOEvent, int id, string name, object[] args) |
|
{ |
|
var memBuffer = BufferPool.Get(256, true); |
|
var stream = new BestHTTP.Extensions.BufferPoolMemoryStream(memBuffer, 0, memBuffer.Length, true, true, false, true); |
|
|
|
var buffer = BufferPool.Get(MsgPackWriter.DEFAULT_BUFFER_SIZE, true); |
|
|
|
var context = new SerializationContext |
|
{ |
|
Options = SerializationOptions.SuppressTypeInformation, |
|
EnumSerializerFactory = (enumType) => new EnumNumberSerializer(enumType)/*, |
|
ExtensionTypeHandler = CustomMessagePackExtensionTypeHandler.Instance*/ |
|
}; |
|
|
|
var writer = new MsgPackWriter(stream, context, buffer); |
|
|
|
switch (socketIOEvent) |
|
{ |
|
case SocketIOEventTypes.Connect: |
|
// No Data | Object |
|
if (args != null && args.Length > 0 && args[0] != null) |
|
writer.WriteObjectBegin(id >= 0 ? 4 : 3); |
|
else |
|
writer.WriteObjectBegin(id >= 0 ? 3 : 2); |
|
break; |
|
|
|
case SocketIOEventTypes.Disconnect: writer.WriteObjectBegin(id > 0 ? 3 : 2); break; |
|
case SocketIOEventTypes.Error: writer.WriteObjectBegin(id > 0 ? 4 : 3); break; |
|
default: writer.WriteObjectBegin(id >= 0 ? 4 : 3); break; |
|
} |
|
|
|
writer.WriteMember("type"); |
|
writer.Write((int)socketIOEvent); |
|
|
|
writer.WriteMember("nsp"); |
|
writer.Write(socket.Namespace); |
|
|
|
if (id >= 0) |
|
{ |
|
writer.WriteMember("id"); |
|
writer.Write(id); |
|
} |
|
|
|
switch (socketIOEvent) |
|
{ |
|
case SocketIOEventTypes.Connect: |
|
// No Data | Object |
|
if (args != null && args.Length > 0 && args[0] != null) |
|
{ |
|
writer.WriteMember("data"); |
|
writer.WriteValue(args[0], args[0].GetType()); |
|
} |
|
break; |
|
|
|
case SocketIOEventTypes.Disconnect: |
|
// No Data |
|
break; |
|
|
|
case SocketIOEventTypes.Error: |
|
writer.WriteMember("data"); |
|
|
|
// String | Object |
|
if (args != null && args.Length > 0) |
|
writer.WriteValue(args[0], args[0].GetType()); |
|
else |
|
{ |
|
writer.WriteObjectBegin(0); |
|
writer.WriteObjectEnd(); |
|
} |
|
break; |
|
|
|
default: |
|
writer.WriteMember("data"); |
|
|
|
// Array |
|
|
|
int argCount = (args != null ? args.Length : 0); |
|
writer.WriteArrayBegin(!string.IsNullOrEmpty(name) ? 1 + argCount : argCount); |
|
|
|
if (!string.IsNullOrEmpty(name)) |
|
writer.Write(name); |
|
|
|
if (args != null && args.Length > 0) |
|
{ |
|
foreach (var arg in args) |
|
{ |
|
if (arg != null) |
|
writer.WriteValue(arg, arg.GetType()); |
|
else |
|
writer.WriteNull(); |
|
} |
|
} |
|
|
|
writer.WriteArrayEnd(); |
|
break; |
|
} |
|
|
|
writer.WriteObjectEnd(); |
|
|
|
writer.Flush(); |
|
|
|
BufferPool.Release(buffer); |
|
|
|
// get how much bytes got written to the buffer |
|
int length = (int)stream.Position; |
|
|
|
buffer = stream.GetBuffer(); |
|
return new OutgoingPacket { PayloadData = new BufferSegment(buffer, 0, length) }; |
|
} |
|
} |
|
} |
|
|
|
#endif
|
|
|