|  |  |  |  | #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 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// Event type of this packet on the transport layer. | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         public TransportEventTypes TransportEvent { get; private set; } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// The packet's type in the Socket.IO protocol. | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         public SocketIOEventTypes SocketIOEvent { get; private set; } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// How many attachment should have this packet. | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         public int AttachmentCount { get; private set; } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// The internal ack-id of this packet. | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         public int Id { get; private set; } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// The sender namespace's name. | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         public string Namespace { get; private set; } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// The payload as a Json string. | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         public string Payload { get; private set; } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// The decoded event name from the payload string. | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         public string EventName { get; private set; } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// All binary data attached to this event. | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         public List<byte[]> Attachments { get { return attachments; } set { attachments = value; AttachmentCount = attachments != null ? attachments.Count : 0; } } | 
					
						
							|  |  |  |  |         private List<byte[]> attachments; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// Property to check whether all attachments are received to this packet. | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         public bool HasAllAttachment { get { return Attachments != null && Attachments.Count == AttachmentCount; } } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// True if it's already decoded. The DecodedArgs still can be null after the Decode call. | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         public bool IsDecoded { get; private set; } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// The decoded arguments from the result of a Json string -> c# object convert. | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         public object[] DecodedArgs { get; private set; } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         #endregion | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         #region Constructors | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// Internal constructor. Don't use it directly! | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         internal Packet() | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |             this.TransportEvent = TransportEventTypes.Unknown; | 
					
						
							|  |  |  |  |             this.SocketIOEvent = SocketIOEventTypes.Unknown; | 
					
						
							|  |  |  |  |             this.Payload = string.Empty; | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// Internal constructor. Don't use it directly! | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         internal Packet(string from) | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |             this.Parse(from); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// Internal constructor. Don't use it directly! | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         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; | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// Will set and return with the EventName from the packet's Payload string. | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         public string DecodeEventName() | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |             // Already decoded | 
					
						
							|  |  |  |  |             if (!string.IsNullOrEmpty(EventName)) | 
					
						
							|  |  |  |  |                 return EventName; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // No Payload to decode | 
					
						
							|  |  |  |  |             if (string.IsNullOrEmpty(Payload)) | 
					
						
							|  |  |  |  |                 return string.Empty; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Not array encoded, we can't decode | 
					
						
							|  |  |  |  |             if (Payload[0] != '[') | 
					
						
							|  |  |  |  |                 return string.Empty; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             int idx = 1; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Search for the string-begin mark( ' or " chars) | 
					
						
							|  |  |  |  |             while (Payload.Length > idx && Payload[idx] != '"' && Payload[idx] != '\'') | 
					
						
							|  |  |  |  |                 idx++; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Reached the end of the string | 
					
						
							|  |  |  |  |             if (Payload.Length <= idx) | 
					
						
							|  |  |  |  |                 return string.Empty; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             int startIdx = ++idx; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Search for the trailing mark of the string | 
					
						
							|  |  |  |  |             while (Payload.Length > idx && Payload[idx] != '"' && Payload[idx] != '\'') | 
					
						
							|  |  |  |  |                 idx++; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Reached the end of the string | 
					
						
							|  |  |  |  |             if (Payload.Length <= idx) | 
					
						
							|  |  |  |  |                 return string.Empty; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             return EventName = Payload.Substring(startIdx, idx - startIdx); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         public string RemoveEventName(bool removeArrayMarks) | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |             // No Payload to decode | 
					
						
							|  |  |  |  |             if (string.IsNullOrEmpty(Payload)) | 
					
						
							|  |  |  |  |                 return string.Empty; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Not array encoded, we can't decode | 
					
						
							|  |  |  |  |             if (Payload[0] != '[') | 
					
						
							|  |  |  |  |                 return string.Empty; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             int idx = 1; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Search for the string-begin mark( ' or " chars) | 
					
						
							|  |  |  |  |             while (Payload.Length > idx && Payload[idx] != '"' && Payload[idx] != '\'') | 
					
						
							|  |  |  |  |                 idx++; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Reached the end of the string | 
					
						
							|  |  |  |  |             if (Payload.Length <= idx) | 
					
						
							|  |  |  |  |                 return string.Empty; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             int startIdx = idx; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Search for end of first element, or end of the array marks | 
					
						
							|  |  |  |  |             while (Payload.Length > idx && Payload[idx] != ',' && Payload[idx] != ']') | 
					
						
							|  |  |  |  |                 idx++; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Reached the end of the string | 
					
						
							|  |  |  |  |             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; | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// Will switch the "{'_placeholder':true,'num':X}" to a the index num X. | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         /// <returns>True if successfully reconstructed, false otherwise.</returns> | 
					
						
							|  |  |  |  |         public bool ReconstructAttachmentAsIndex() | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |             //"452-["multiImage",{"image":true,"buffer1":{"_placeholder":true,"num":0},"buffer2":{"_placeholder":true,"num":1}}]" | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             return PlaceholderReplacer((json, obj) => | 
					
						
							|  |  |  |  |             { | 
					
						
							|  |  |  |  |                 int idx = Convert.ToInt32(obj["num"]); | 
					
						
							|  |  |  |  |                 this.Payload = this.Payload.Replace(json, idx.ToString()); | 
					
						
							|  |  |  |  |                 this.IsDecoded = false; | 
					
						
							|  |  |  |  |             }); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// Will switch the "{'_placeholder':true,'num':X}" to a the data as a base64 encoded string. | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         /// <returns>True if successfully reconstructed, false otherwise.</returns> | 
					
						
							|  |  |  |  |         public bool ReconstructAttachmentAsBase64() | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |             //"452-["multiImage",{"image":true,"buffer1":{"_placeholder":true,"num":0},"buffer2":{"_placeholder":true,"num":1}}]" | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             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 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// Parse the packet from a server sent textual data. The Payload will be the raw json string. | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         internal void Parse(string from) | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |             int idx = 0; | 
					
						
							|  |  |  |  |             this.TransportEvent = (TransportEventTypes)(int)char.GetNumericValue(from, idx++); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             if (from.Length > idx && char.GetNumericValue(from, idx) >= 0.0) | 
					
						
							|  |  |  |  |                 this.SocketIOEvent = (SocketIOEventTypes)(int)char.GetNumericValue(from, idx++); | 
					
						
							|  |  |  |  |             else | 
					
						
							|  |  |  |  |                 this.SocketIOEvent = SocketIOEventTypes.Unknown; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Parse Attachment | 
					
						
							|  |  |  |  |             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; | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Parse Namespace | 
					
						
							|  |  |  |  |             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 = "/"; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Parse Id | 
					
						
							|  |  |  |  |             if (from.Length > idx && char.GetNumericValue(from[idx]) >= 0) | 
					
						
							|  |  |  |  |             { | 
					
						
							|  |  |  |  |                 int startIdx = idx++; | 
					
						
							|  |  |  |  |                 while (from.Length > idx && char.GetNumericValue(from[idx]) >= 0) | 
					
						
							|  |  |  |  |                     idx++; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 int id = 0; | 
					
						
							|  |  |  |  |                 int.TryParse(from.Substring(startIdx, idx - startIdx), out id); | 
					
						
							|  |  |  |  |                 this.Id = id; | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // What left is the payload data | 
					
						
							|  |  |  |  |             if (from.Length > idx) | 
					
						
							|  |  |  |  |                 this.Payload = from.Substring(idx); | 
					
						
							|  |  |  |  |             else | 
					
						
							|  |  |  |  |                 this.Payload = string.Empty; | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// Encodes this packet to a Socket.IO formatted string. | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         internal string Encode() | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |             StringBuilder builder = new StringBuilder(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Set to Message if not set, and we are sending attachments | 
					
						
							|  |  |  |  |             if (this.TransportEvent == TransportEventTypes.Unknown && this.AttachmentCount > 0) | 
					
						
							|  |  |  |  |                 this.TransportEvent = TransportEventTypes.Message; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             if (this.TransportEvent != TransportEventTypes.Unknown) | 
					
						
							|  |  |  |  |                 builder.Append(((int)this.TransportEvent).ToString()); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Set to BinaryEvent if not set, and we are sending attachments | 
					
						
							|  |  |  |  |             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("-"); | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // 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 (this.Namespace != "/") | 
					
						
							|  |  |  |  |             { | 
					
						
							|  |  |  |  |                 builder.Append(this.Namespace); | 
					
						
							|  |  |  |  |                 nspAdded = true; | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // ack id, if any | 
					
						
							|  |  |  |  |             if (this.Id != 0) | 
					
						
							|  |  |  |  |             { | 
					
						
							|  |  |  |  |                 if (nspAdded) | 
					
						
							|  |  |  |  |                 { | 
					
						
							|  |  |  |  |                     builder.Append(","); | 
					
						
							|  |  |  |  |                     nspAdded = false; | 
					
						
							|  |  |  |  |                 } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 builder.Append(this.Id.ToString()); | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // payload | 
					
						
							|  |  |  |  |             if (!string.IsNullOrEmpty(this.Payload)) | 
					
						
							|  |  |  |  |             { | 
					
						
							|  |  |  |  |                 if (nspAdded) | 
					
						
							|  |  |  |  |                 { | 
					
						
							|  |  |  |  |                     builder.Append(","); | 
					
						
							|  |  |  |  |                     nspAdded = false; | 
					
						
							|  |  |  |  |                 } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 builder.Append(this.Payload); | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             return builder.ToString(); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// Encodes this packet to a Socket.IO formatted byte array. | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         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!"); | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Encode it as usual | 
					
						
							|  |  |  |  |             string encoded = Encode(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Convert it to a byte[] | 
					
						
							|  |  |  |  |             byte[] payload = Encoding.UTF8.GetBytes(encoded); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Encode it to a message | 
					
						
							|  |  |  |  |             byte[] buffer = EncodeData(payload, PayloadTypes.Textual, null); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // If there is any attachment, convert them too, and append them after each other | 
					
						
							|  |  |  |  |             if (AttachmentCount != 0) | 
					
						
							|  |  |  |  |             { | 
					
						
							|  |  |  |  |                 int idx = buffer.Length; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 // List to temporarily hold the converted attachments | 
					
						
							|  |  |  |  |                 List<byte[]> attachmentDatas = new List<byte[]>(AttachmentCount); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 // The sum size of the converted attachments to be able to resize our buffer only once. This way we can avoid some GC garbage | 
					
						
							|  |  |  |  |                 int attachmentDataSize = 0; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 // Encode our attachments, and store them in our list | 
					
						
							|  |  |  |  |                 for (int i = 0; i < AttachmentCount; i++) | 
					
						
							|  |  |  |  |                 { | 
					
						
							|  |  |  |  |                     byte[] tmpBuff = EncodeData(Attachments[i], PayloadTypes.Binary, new byte[] { 4 }); | 
					
						
							|  |  |  |  |                     attachmentDatas.Add(tmpBuff); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                     attachmentDataSize += tmpBuff.Length; | 
					
						
							|  |  |  |  |                 } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 // Resize our buffer once | 
					
						
							|  |  |  |  |                 Array.Resize(ref buffer, buffer.Length + attachmentDataSize); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 // And copy all data into it | 
					
						
							|  |  |  |  |                 for (int i = 0; i < AttachmentCount; ++i) | 
					
						
							|  |  |  |  |                 { | 
					
						
							|  |  |  |  |                     byte[] data = attachmentDatas[i]; | 
					
						
							|  |  |  |  |                     Array.Copy(data, 0, buffer, idx, data.Length); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                     idx += data.Length; | 
					
						
							|  |  |  |  |                 } | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Return the buffer | 
					
						
							|  |  |  |  |             return buffer; | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// Will add the byte[] that the server sent to the attachments list. | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         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 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// Encodes a byte array to a Socket.IO binary encoded message | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         private byte[] EncodeData(byte[] data, PayloadTypes type, byte[] afterHeaderData) | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |             // Packet binary encoding: | 
					
						
							|  |  |  |  |             // [          0|1         ][            length of data           ][    FF    ][data] | 
					
						
							|  |  |  |  |             // <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number 255><data> | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Get the length of the payload. Socket.IO uses a wasteful encoding to send the length of the data. | 
					
						
							|  |  |  |  |             // If the data is 16 bytes we have to send the length as two bytes: byte value of the character '1' and byte value of the character '6'. | 
					
						
							|  |  |  |  |             // Instead of just one byte: 0xF. If the payload is 123 bytes, we can't send as 0x7B... | 
					
						
							|  |  |  |  |             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]); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // We need another buffer to store the final data | 
					
						
							|  |  |  |  |             byte[] buffer = new byte[data.Length + len.Length + 2 + afterHeaderLength]; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // The payload is textual -> 0 | 
					
						
							|  |  |  |  |             buffer[0] = (byte)type; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Copy the length of the data | 
					
						
							|  |  |  |  |             for (int cv = 0; cv < len.Length; ++cv) | 
					
						
							|  |  |  |  |                 buffer[1 + cv] = len[cv]; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             int idx = 1 + len.Length; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // End of the header data | 
					
						
							|  |  |  |  |             buffer[idx++] = 0xFF; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             if (afterHeaderData != null && afterHeaderData.Length > 0) | 
					
						
							|  |  |  |  |             { | 
					
						
							|  |  |  |  |                 Array.Copy(afterHeaderData, 0, buffer, idx, afterHeaderData.Length); | 
					
						
							|  |  |  |  |                 idx += afterHeaderData.Length; | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Copy our payload data to the buffer | 
					
						
							|  |  |  |  |             Array.Copy(data, 0, buffer, idx, data.Length); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             return buffer; | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// Searches for the "{'_placeholder':true,'num':X}" string, and will call the given action to modify the PayLoad | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         private bool PlaceholderReplacer(Action<string, Dictionary<string, object>> onFound) | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |             if (string.IsNullOrEmpty(this.Payload)) | 
					
						
							|  |  |  |  |                 return false; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // Find the first index of the "_placeholder" str | 
					
						
							|  |  |  |  |             int placeholderIdx = this.Payload.IndexOf(Placeholder); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             while (placeholderIdx >= 0) | 
					
						
							|  |  |  |  |             { | 
					
						
							|  |  |  |  |                 // Find the object-start token | 
					
						
							|  |  |  |  |                 int startIdx = placeholderIdx; | 
					
						
							|  |  |  |  |                 while (this.Payload[startIdx] != '{') | 
					
						
							|  |  |  |  |                     startIdx--; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 // Find the object-end token | 
					
						
							|  |  |  |  |                 int endIdx = placeholderIdx; | 
					
						
							|  |  |  |  |                 while (this.Payload.Length > endIdx && this.Payload[endIdx] != '}') | 
					
						
							|  |  |  |  |                     endIdx++; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 // We reached the end | 
					
						
							|  |  |  |  |                 if (this.Payload.Length <= endIdx) | 
					
						
							|  |  |  |  |                     return false; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 // Get the object, and decode it | 
					
						
							|  |  |  |  |                 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; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 // Check for presence and value of _placeholder | 
					
						
							|  |  |  |  |                 object value; | 
					
						
							|  |  |  |  |                 if (!obj.TryGetValue(Placeholder, out value) || | 
					
						
							|  |  |  |  |                     !(bool)value) | 
					
						
							|  |  |  |  |                     return false; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 // Check for presence of num | 
					
						
							|  |  |  |  |                 if (!obj.TryGetValue("num", out value)) | 
					
						
							|  |  |  |  |                     return false; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 // Let do, what we have to do | 
					
						
							|  |  |  |  |                 onFound(placeholderJson, obj); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 // Find the next attachment if there is any | 
					
						
							|  |  |  |  |                 placeholderIdx = this.Payload.IndexOf(Placeholder); | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             return true; | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         #endregion | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         #region Overrides and Interface Implementations | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// Returns with the Payload of this packet. | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         public override string ToString() | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |             return this.Payload; | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /// <summary> | 
					
						
							|  |  |  |  |         /// Will clone this packet to an identical packet instance. | 
					
						
							|  |  |  |  |         /// </summary> | 
					
						
							|  |  |  |  |         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 |