123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611 |
- #if !BESTHTTP_DISABLE_SOCKETIO
- using System.Text;
- namespace BestHTTP.SocketIO
- {
- using System;
- using System.Collections.Generic;
- using BestHTTP.JSON;
- public sealed class Packet
- {
- private enum PayloadTypes : byte
- {
- Textual = 0,
- Binary = 1
- }
- public const string Placeholder = "_placeholder";
- #region Public properties
-
-
-
- public TransportEventTypes TransportEvent { get; private set; }
-
-
-
- public SocketIOEventTypes SocketIOEvent { get; private set; }
-
-
-
- public int AttachmentCount { get; private set; }
-
-
-
- public int Id { get; private set; }
-
-
-
- public string Namespace { get; private set; }
-
-
-
- public string Payload { get; private set; }
-
-
-
- public string EventName { get; private set; }
-
-
-
- public List<byte[]> Attachments { get { return attachments; } set { attachments = value; AttachmentCount = attachments != null ? attachments.Count : 0; } }
- private List<byte[]> attachments;
-
-
-
- public bool HasAllAttachment { get { return Attachments != null && Attachments.Count == AttachmentCount; } }
-
-
-
- public bool IsDecoded { get; private set; }
-
-
-
- public object[] DecodedArgs { get; private set; }
- #endregion
- #region Constructors
-
-
-
- internal Packet()
- {
- this.TransportEvent = TransportEventTypes.Unknown;
- this.SocketIOEvent = SocketIOEventTypes.Unknown;
- this.Payload = string.Empty;
- }
-
-
-
- internal Packet(string from)
- {
- this.Parse(from);
- }
-
-
-
- public Packet(TransportEventTypes transportEvent, SocketIOEventTypes packetType, string nsp, string payload, int attachment = 0, int id = 0)
- {
- this.TransportEvent = transportEvent;
- this.SocketIOEvent = packetType;
- this.Namespace = nsp;
- this.Payload = payload;
- this.AttachmentCount = attachment;
- this.Id = id;
- }
- #endregion
- #region Public Functions
- public object[] Decode(BestHTTP.SocketIO.JsonEncoders.IJsonEncoder encoder)
- {
- if (IsDecoded || encoder == null)
- return DecodedArgs;
- IsDecoded = true;
- if (string.IsNullOrEmpty(Payload))
- return DecodedArgs;
- List<object> decoded = encoder.Decode(Payload);
- if (decoded != null && decoded.Count > 0)
- {
- if (this.SocketIOEvent == SocketIOEventTypes.Ack || this.SocketIOEvent == SocketIOEventTypes.BinaryAck)
- DecodedArgs = decoded.ToArray();
- else
- {
- decoded.RemoveAt(0);
- DecodedArgs = decoded.ToArray();
- }
- }
- return DecodedArgs;
- }
-
-
-
- public string DecodeEventName()
- {
-
- if (!string.IsNullOrEmpty(EventName))
- return EventName;
-
- if (string.IsNullOrEmpty(Payload))
- return string.Empty;
-
- if (Payload[0] != '[')
- return string.Empty;
- int idx = 1;
-
- while (Payload.Length > idx && Payload[idx] != '"' && Payload[idx] != '\'')
- idx++;
-
- if (Payload.Length <= idx)
- return string.Empty;
- int startIdx = ++idx;
-
- while (Payload.Length > idx && Payload[idx] != '"' && Payload[idx] != '\'')
- idx++;
-
- if (Payload.Length <= idx)
- return string.Empty;
- return EventName = Payload.Substring(startIdx, idx - startIdx);
- }
- public string RemoveEventName(bool removeArrayMarks)
- {
-
- if (string.IsNullOrEmpty(Payload))
- return string.Empty;
-
- if (Payload[0] != '[')
- return string.Empty;
- int idx = 1;
-
- while (Payload.Length > idx && Payload[idx] != '"' && Payload[idx] != '\'')
- idx++;
-
- if (Payload.Length <= idx)
- return string.Empty;
- int startIdx = idx;
-
- while (Payload.Length > idx && Payload[idx] != ',' && Payload[idx] != ']')
- idx++;
-
- if (Payload.Length <= ++idx)
- return string.Empty;
- string payload = Payload.Remove(startIdx, idx - startIdx);
- if (removeArrayMarks)
- payload = payload.Substring(1, payload.Length - 2);
- return payload;
- }
-
-
-
-
- public bool ReconstructAttachmentAsIndex()
- {
-
- return PlaceholderReplacer((json, obj) =>
- {
- int idx = Convert.ToInt32(obj["num"]);
- this.Payload = this.Payload.Replace(json, idx.ToString());
- this.IsDecoded = false;
- });
- }
-
-
-
-
- public bool ReconstructAttachmentAsBase64()
- {
-
- if (!HasAllAttachment)
- return false;
- return PlaceholderReplacer((json, obj) =>
- {
- int idx = Convert.ToInt32(obj["num"]);
- this.Payload = this.Payload.Replace(json, string.Format("\"{0}\"", Convert.ToBase64String(this.Attachments[idx])));
- this.IsDecoded = false;
- });
- }
- #endregion
- #region Internal Functions
-
-
-
- internal void Parse(string from)
- {
- int idx = 0;
- this.TransportEvent = (TransportEventTypes)ToInt(from[idx++]);
- if (from.Length > idx && ToInt(from[idx]) >= 0)
- this.SocketIOEvent = (SocketIOEventTypes)ToInt(from[idx++]);
- else
- this.SocketIOEvent = SocketIOEventTypes.Unknown;
-
- if (this.SocketIOEvent == SocketIOEventTypes.BinaryEvent || this.SocketIOEvent == SocketIOEventTypes.BinaryAck)
- {
- int endIdx = from.IndexOf('-', idx);
- if (endIdx == -1)
- endIdx = from.Length;
- int attachment = 0;
- int.TryParse(from.Substring(idx, endIdx - idx), out attachment);
- this.AttachmentCount = attachment;
- idx = endIdx + 1;
- }
-
- if (from.Length > idx && from[idx] == '/')
- {
- int endIdx = from.IndexOf(',', idx);
- if (endIdx == -1)
- endIdx = from.Length;
- this.Namespace = from.Substring(idx, endIdx - idx);
- idx = endIdx + 1;
- }
- else
- this.Namespace = "/";
-
- if (from.Length > idx && ToInt(from[idx]) >= 0)
- {
- int startIdx = idx++;
- while (from.Length > idx && ToInt(from[idx]) >= 0)
- idx++;
- int id = 0;
- int.TryParse(from.Substring(startIdx, idx - startIdx), out id);
- this.Id = id;
- }
-
- if (from.Length > idx)
- this.Payload = from.Substring(idx);
- else
- this.Payload = string.Empty;
- }
-
-
-
-
- private int ToInt(char ch)
- {
- int charValue = Convert.ToInt32(ch);
- int num = charValue - '0';
- if (num < 0 || num > 9)
- return -1;
- return num;
- }
-
-
-
- internal string Encode()
- {
- StringBuilder builder = new StringBuilder();
-
- if (this.TransportEvent == TransportEventTypes.Unknown && this.AttachmentCount > 0)
- this.TransportEvent = TransportEventTypes.Message;
- if (this.TransportEvent != TransportEventTypes.Unknown)
- builder.Append(((int)this.TransportEvent).ToString());
-
- if (this.SocketIOEvent == SocketIOEventTypes.Unknown && this.AttachmentCount > 0)
- this.SocketIOEvent = SocketIOEventTypes.BinaryEvent;
- if (this.SocketIOEvent != SocketIOEventTypes.Unknown)
- builder.Append(((int)this.SocketIOEvent).ToString());
- if (this.SocketIOEvent == SocketIOEventTypes.BinaryEvent || this.SocketIOEvent == SocketIOEventTypes.BinaryAck)
- {
- builder.Append(this.AttachmentCount.ToString());
- builder.Append("-");
- }
-
-
- bool nspAdded = false;
- if (this.Namespace != "/")
- {
- builder.Append(this.Namespace);
- nspAdded = true;
- }
-
- if (this.Id != 0)
- {
- if (nspAdded)
- {
- builder.Append(",");
- nspAdded = false;
- }
- builder.Append(this.Id.ToString());
- }
-
- if (!string.IsNullOrEmpty(this.Payload))
- {
- if (nspAdded)
- {
- builder.Append(",");
- nspAdded = false;
- }
- builder.Append(this.Payload);
- }
- return builder.ToString();
- }
-
-
-
- internal byte[] EncodeBinary()
- {
- if (AttachmentCount != 0 || (Attachments != null && Attachments.Count != 0))
- {
- if (Attachments == null)
- throw new ArgumentException("packet.Attachments are null!");
- if (AttachmentCount != Attachments.Count)
- throw new ArgumentException("packet.AttachmentCount != packet.Attachments.Count. Use the packet.AddAttachment function to add data to a packet!");
- }
-
- string encoded = Encode();
-
- byte[] payload = Encoding.UTF8.GetBytes(encoded);
-
- byte[] buffer = EncodeData(payload, PayloadTypes.Textual, null);
-
- if (AttachmentCount != 0)
- {
- int idx = buffer.Length;
-
- List<byte[]> attachmentDatas = new List<byte[]>(AttachmentCount);
-
- int attachmentDataSize = 0;
-
- for (int i = 0; i < AttachmentCount; i++)
- {
- byte[] tmpBuff = EncodeData(Attachments[i], PayloadTypes.Binary, new byte[] { 4 });
- attachmentDatas.Add(tmpBuff);
- attachmentDataSize += tmpBuff.Length;
- }
-
- Array.Resize(ref buffer, buffer.Length + attachmentDataSize);
-
- for (int i = 0; i < AttachmentCount; ++i)
- {
- byte[] data = attachmentDatas[i];
- Array.Copy(data, 0, buffer, idx, data.Length);
- idx += data.Length;
- }
- }
-
- return buffer;
- }
-
-
-
- internal void AddAttachmentFromServer(byte[] data, bool copyFull)
- {
- if (data == null || data.Length == 0)
- return;
- if (this.attachments == null)
- this.attachments = new List<byte[]>(this.AttachmentCount);
- if (copyFull)
- this.Attachments.Add(data);
- else
- {
- byte[] buff = new byte[data.Length - 1];
- Array.Copy(data, 1, buff, 0, data.Length - 1);
- this.Attachments.Add(buff);
- }
- }
- #endregion
- #region Private Helper Functions
-
-
-
- private byte[] EncodeData(byte[] data, PayloadTypes type, byte[] afterHeaderData)
- {
-
-
-
-
-
-
- int afterHeaderLength = (afterHeaderData != null ? afterHeaderData.Length : 0);
- string lenStr = (data.Length + afterHeaderLength).ToString();
- byte[] len = new byte[lenStr.Length];
- for (int cv = 0; cv < lenStr.Length; ++cv)
- len[cv] = (byte)char.GetNumericValue(lenStr[cv]);
-
- byte[] buffer = new byte[data.Length + len.Length + 2 + afterHeaderLength];
-
- buffer[0] = (byte)type;
-
- for (int cv = 0; cv < len.Length; ++cv)
- buffer[1 + cv] = len[cv];
- int idx = 1 + len.Length;
-
- buffer[idx++] = 0xFF;
- if (afterHeaderData != null && afterHeaderData.Length > 0)
- {
- Array.Copy(afterHeaderData, 0, buffer, idx, afterHeaderData.Length);
- idx += afterHeaderData.Length;
- }
-
- Array.Copy(data, 0, buffer, idx, data.Length);
- return buffer;
- }
-
-
-
- private bool PlaceholderReplacer(Action<string, Dictionary<string, object>> onFound)
- {
- if (string.IsNullOrEmpty(this.Payload))
- return false;
-
- int placeholderIdx = this.Payload.IndexOf(Placeholder);
- while (placeholderIdx >= 0)
- {
-
- int startIdx = placeholderIdx;
- while (this.Payload[startIdx] != '{')
- startIdx--;
-
- int endIdx = placeholderIdx;
- while (this.Payload.Length > endIdx && this.Payload[endIdx] != '}')
- endIdx++;
-
- if (this.Payload.Length <= endIdx)
- return false;
-
- string placeholderJson = this.Payload.Substring(startIdx, endIdx - startIdx + 1);
- bool success = false;
- Dictionary<string, object> obj = Json.Decode(placeholderJson, ref success) as Dictionary<string, object>;
- if (!success)
- return false;
-
- object value;
- if (!obj.TryGetValue(Placeholder, out value) ||
- !(bool)value)
- return false;
-
- if (!obj.TryGetValue("num", out value))
- return false;
-
- onFound(placeholderJson, obj);
-
- placeholderIdx = this.Payload.IndexOf(Placeholder);
- }
- return true;
- }
- #endregion
- #region Overrides and Interface Implementations
-
-
-
- public override string ToString()
- {
- return this.Payload;
- }
-
-
-
- internal Packet Clone()
- {
- Packet packet = new Packet(this.TransportEvent, this.SocketIOEvent, this.Namespace, this.Payload, 0, this.Id);
- packet.EventName = this.EventName;
- packet.AttachmentCount = this.AttachmentCount;
- packet.attachments = this.attachments;
- return packet;
- }
- #endregion
- }
- }
- #endif
|