diff --git a/Examples/MeshReceivingUnityClient/.gitignore b/Examples/MeshReceivingUnityClient/.gitignore new file mode 100644 index 0000000..75ad779 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/.gitignore @@ -0,0 +1,37 @@ +# ---------------[ Unity generated ]------------------ # +[Ll]ibrary/ +[Tt]emp/ +[Oo]bj/ +UnityGenerated/ +#ProjectSettings/*.asset# + +# ----[ Visual Studio / MonoDevelop generated ]------- # + +ExportedObj/ +*.svd +*.userprefs +*.csproj +*.pidb +*.suo +*.sln +*.user +*.unityproj +*.booproj + +# -------------[ OS generated ]------------------------ # +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +#Icon? +#Icon?.meta +ehthumbs.db +Thumbs.db + +# -------------[ Build ]------------------------ # +*.apk +*.unity3d +iOSApp/ +Builds/ +SnapShots/ diff --git a/MeshStreaming/CustomMesh.cs b/Examples/MeshReceivingUnityClient/Assets/Dlls/CustomMesh.cs similarity index 100% rename from MeshStreaming/CustomMesh.cs rename to Examples/MeshReceivingUnityClient/Assets/Dlls/CustomMesh.cs diff --git a/Examples/MeshReceivingUnityClient/Assets/Dlls/ZeroFormatter.Interfaces.dll b/Examples/MeshReceivingUnityClient/Assets/Dlls/ZeroFormatter.Interfaces.dll new file mode 100644 index 0000000..11d1aff Binary files /dev/null and b/Examples/MeshReceivingUnityClient/Assets/Dlls/ZeroFormatter.Interfaces.dll differ diff --git a/Examples/MeshReceivingUnityClient/Assets/Dlls/ZeroFormatter.dll b/Examples/MeshReceivingUnityClient/Assets/Dlls/ZeroFormatter.dll new file mode 100644 index 0000000..ee6c61d Binary files /dev/null and b/Examples/MeshReceivingUnityClient/Assets/Dlls/ZeroFormatter.dll differ diff --git a/Examples/MeshReceivingUnityClient/Assets/Dlls/ZeroFormatterGenerated.cs b/Examples/MeshReceivingUnityClient/Assets/Dlls/ZeroFormatterGenerated.cs new file mode 100644 index 0000000..ae74577 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/Dlls/ZeroFormatterGenerated.cs @@ -0,0 +1,221 @@ +#pragma warning disable 618 +#pragma warning disable 612 +#pragma warning disable 414 +#pragma warning disable 168 +namespace ZeroFormatter +{ + using global::System; + using global::System.Collections.Generic; + using global::System.Linq; + using global::ZeroFormatter.Formatters; + using global::ZeroFormatter.Internal; + using global::ZeroFormatter.Segments; + using global::ZeroFormatter.Comparers; + + public static partial class ZeroFormatterInitializer + { + static bool registered = false; + + [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.BeforeSceneLoad)] + public static void Register() + { + if(registered) return; + registered = true; + // Enums + // Objects + ZeroFormatter.Formatters.Formatter.Register(new ZeroFormatter.DynamicObjectSegments.MeshStreaming.CustomMeshFormatter()); + // Structs + // Unions + // Generics + ZeroFormatter.Formatters.Formatter.RegisterList(); + ZeroFormatter.Formatters.Formatter.RegisterList(); + } + } +} +#pragma warning restore 168 +#pragma warning restore 414 +#pragma warning restore 618 +#pragma warning restore 612 +#pragma warning disable 618 +#pragma warning disable 612 +#pragma warning disable 414 +#pragma warning disable 168 +namespace ZeroFormatter.DynamicObjectSegments.MeshStreaming +{ + using global::System; + using global::ZeroFormatter.Formatters; + using global::ZeroFormatter.Internal; + using global::ZeroFormatter.Segments; + + public class CustomMeshFormatter : Formatter + where TTypeResolver : ITypeResolver, new() + { + public override int? GetLength() + { + return null; + } + + public override int Serialize(ref byte[] bytes, int offset, global::MeshStreaming.CustomMesh value) + { + var segment = value as IZeroFormatterSegment; + if (segment != null) + { + return segment.Serialize(ref bytes, offset); + } + else if (value == null) + { + BinaryUtil.WriteInt32(ref bytes, offset, -1); + return 4; + } + else + { + var startOffset = offset; + + offset += (8 + 4 * (3 + 1)); + offset += ObjectSegmentHelper.SerializeFromFormatter>(ref bytes, startOffset, offset, 0, value.vertices); + offset += ObjectSegmentHelper.SerializeFromFormatter>(ref bytes, startOffset, offset, 1, value.uvs); + offset += ObjectSegmentHelper.SerializeFromFormatter>(ref bytes, startOffset, offset, 2, value.normals); + offset += ObjectSegmentHelper.SerializeFromFormatter>(ref bytes, startOffset, offset, 3, value.faces); + + return ObjectSegmentHelper.WriteSize(ref bytes, startOffset, offset, 3); + } + } + + public override global::MeshStreaming.CustomMesh Deserialize(ref byte[] bytes, int offset, global::ZeroFormatter.DirtyTracker tracker, out int byteSize) + { + byteSize = BinaryUtil.ReadInt32(ref bytes, offset); + if (byteSize == -1) + { + byteSize = 4; + return null; + } + return new CustomMeshObjectSegment(tracker, new ArraySegment(bytes, offset, byteSize)); + } + } + + public class CustomMeshObjectSegment : global::MeshStreaming.CustomMesh, IZeroFormatterSegment + where TTypeResolver : ITypeResolver, new() + { + static readonly int[] __elementSizes = new int[]{ 0, 0, 0, 0 }; + + readonly ArraySegment __originalBytes; + readonly global::ZeroFormatter.DirtyTracker __tracker; + readonly int __binaryLastIndex; + readonly byte[] __extraFixedBytes; + + global::System.Collections.Generic.IList _vertices; + global::System.Collections.Generic.IList _uvs; + global::System.Collections.Generic.IList _normals; + global::System.Collections.Generic.IList _faces; + + // 0 + public override global::System.Collections.Generic.IList vertices + { + get + { + return _vertices; + } + set + { + __tracker.Dirty(); + _vertices = value; + } + } + + // 1 + public override global::System.Collections.Generic.IList uvs + { + get + { + return _uvs; + } + set + { + __tracker.Dirty(); + _uvs = value; + } + } + + // 2 + public override global::System.Collections.Generic.IList normals + { + get + { + return _normals; + } + set + { + __tracker.Dirty(); + _normals = value; + } + } + + // 3 + public override global::System.Collections.Generic.IList faces + { + get + { + return _faces; + } + set + { + __tracker.Dirty(); + _faces = value; + } + } + + + public CustomMeshObjectSegment(global::ZeroFormatter.DirtyTracker dirtyTracker, ArraySegment originalBytes) + { + var __array = originalBytes.Array; + + this.__originalBytes = originalBytes; + this.__tracker = dirtyTracker = dirtyTracker.CreateChild(); + this.__binaryLastIndex = BinaryUtil.ReadInt32(ref __array, originalBytes.Offset + 4); + + this.__extraFixedBytes = ObjectSegmentHelper.CreateExtraFixedBytes(this.__binaryLastIndex, 3, __elementSizes); + + _vertices = ObjectSegmentHelper.DeserializeSegment>(originalBytes, 0, __binaryLastIndex, __tracker); + _uvs = ObjectSegmentHelper.DeserializeSegment>(originalBytes, 1, __binaryLastIndex, __tracker); + _normals = ObjectSegmentHelper.DeserializeSegment>(originalBytes, 2, __binaryLastIndex, __tracker); + _faces = ObjectSegmentHelper.DeserializeSegment>(originalBytes, 3, __binaryLastIndex, __tracker); + } + + public bool CanDirectCopy() + { + return !__tracker.IsDirty; + } + + public ArraySegment GetBufferReference() + { + return __originalBytes; + } + + public int Serialize(ref byte[] targetBytes, int offset) + { + if (__extraFixedBytes != null || __tracker.IsDirty) + { + var startOffset = offset; + offset += (8 + 4 * (3 + 1)); + + offset += ObjectSegmentHelper.SerializeSegment>(ref targetBytes, startOffset, offset, 0, _vertices); + offset += ObjectSegmentHelper.SerializeSegment>(ref targetBytes, startOffset, offset, 1, _uvs); + offset += ObjectSegmentHelper.SerializeSegment>(ref targetBytes, startOffset, offset, 2, _normals); + offset += ObjectSegmentHelper.SerializeSegment>(ref targetBytes, startOffset, offset, 3, _faces); + + return ObjectSegmentHelper.WriteSize(ref targetBytes, startOffset, offset, 3); + } + else + { + return ObjectSegmentHelper.DirectCopyAll(__originalBytes, ref targetBytes, offset); + } + } + } + + +} + +#pragma warning restore 168 +#pragma warning restore 414 +#pragma warning restore 618 +#pragma warning restore 612 diff --git a/Examples/MeshReceivingUnityClient/Assets/Materials/White.mat b/Examples/MeshReceivingUnityClient/Assets/Materials/White.mat new file mode 100644 index 0000000..4b7fa9e Binary files /dev/null and b/Examples/MeshReceivingUnityClient/Assets/Materials/White.mat differ diff --git a/Examples/MeshReceivingUnityClient/Assets/Prefabs/ReceivedMesh.prefab b/Examples/MeshReceivingUnityClient/Assets/Prefabs/ReceivedMesh.prefab new file mode 100644 index 0000000..29716dc Binary files /dev/null and b/Examples/MeshReceivingUnityClient/Assets/Prefabs/ReceivedMesh.prefab differ diff --git a/Examples/MeshReceivingUnityClient/Assets/ProceduralToolkit/MeshDraft.cs b/Examples/MeshReceivingUnityClient/Assets/ProceduralToolkit/MeshDraft.cs new file mode 100644 index 0000000..ff9404a --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/ProceduralToolkit/MeshDraft.cs @@ -0,0 +1,154 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace ProceduralToolkit +{ + /// + /// Helper class for procedural mesh generation + /// + public partial class MeshDraft + { + public string name = ""; + public List vertices = new List(); + public List triangles = new List(); + public List normals = new List(); + public List uv = new List(); + public List colors = new List(); + + public MeshDraft() + { + } + + public MeshDraft(Mesh mesh) + { + name = mesh.name; + vertices.AddRange(mesh.vertices); + triangles.AddRange(mesh.triangles); + normals.AddRange(mesh.normals); + uv.AddRange(mesh.uv); + colors.AddRange(mesh.colors); + } + + /// + /// Adds mesh information from another draft + /// + public void Add(MeshDraft draft) + { + foreach (var triangle in draft.triangles) + { + triangles.Add(triangle + vertices.Count); + } + vertices.AddRange(draft.vertices); + normals.AddRange(draft.normals); + uv.AddRange(draft.uv); + colors.AddRange(draft.colors); + } + + /// + /// Moves draft vertices by + /// + public void Move(Vector3 vector) + { + for (int i = 0; i < vertices.Count; i++) + { + vertices[i] += vector; + } + } + + /// + /// Rotates draft vertices by + /// + public void Rotate(Quaternion rotation) + { + for (int i = 0; i < vertices.Count; i++) + { + vertices[i] = rotation*vertices[i]; + normals[i] = rotation*normals[i]; + } + } + + /// + /// Scales draft vertices uniformly by + /// + public void Scale(float scale) + { + for (int i = 0; i < vertices.Count; i++) + { + vertices[i] *= scale; + } + } + + /// + /// Scales draft vertices non-uniformly by + /// + public void Scale(Vector3 scale) + { + for (int i = 0; i < vertices.Count; i++) + { + var v = vertices[i]; + vertices[i] = new Vector3(v.x*scale.x, v.y*scale.y, v.z*scale.z); + var n = normals[i]; + normals[i] = new Vector3(n.x*scale.x, n.y*scale.y, n.z*scale.z).normalized; + } + } + + /// + /// Paints draft vertices with + /// + public void Paint(Color color) + { + colors.Clear(); + for (int i = 0; i < vertices.Count; i++) + { + colors.Add(color); + } + } + + /// + /// Flips draft faces + /// + public void FlipFaces() + { + FlipTriangles(); + FlipNormals(); + } + + /// + /// Reverses winding order of draft triangles + /// + public void FlipTriangles() + { + for (int i = 0; i < triangles.Count; i += 3) + { + var temp = triangles[i]; + triangles[i] = triangles[i + 1]; + triangles[i + 1] = temp; + } + } + + /// + /// Reverses direction of draft normals + /// + public void FlipNormals() + { + for (int i = 0; i < normals.Count; i++) + { + normals[i] = -normals[i]; + } + } + + /// + /// Creates new mesh from information in draft + /// + public Mesh ToMesh() + { + var mesh = new Mesh {name = name}; + mesh.SetVertices(vertices); + mesh.SetTriangles(triangles, 0); + mesh.SetNormals(normals); + mesh.SetUVs(0, uv); + mesh.SetColors(colors); + return mesh; + } + } +} \ No newline at end of file diff --git a/Examples/MeshReceivingUnityClient/Assets/Scenes/MeshReceiving.unity b/Examples/MeshReceivingUnityClient/Assets/Scenes/MeshReceiving.unity new file mode 100644 index 0000000..4f430f8 Binary files /dev/null and b/Examples/MeshReceivingUnityClient/Assets/Scenes/MeshReceiving.unity differ diff --git a/Examples/MeshReceivingUnityClient/Assets/Scripts/MeshReceiving.cs b/Examples/MeshReceivingUnityClient/Assets/Scripts/MeshReceiving.cs new file mode 100644 index 0000000..4a568fc --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/Scripts/MeshReceiving.cs @@ -0,0 +1,151 @@ +#region License +/* + * TestSocketIO.cs + * + * The MIT License + * + * Copyright (c) 2014 Fabio Panettieri + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using SocketIO; +using ZeroFormatter; +using MeshStreaming; +using ProceduralToolkit; + +public class MeshReceiving : MonoBehaviour +{ + private SocketIOComponent socket; + public GameObject receivedMeshPrefab; + + public void Start() + { + GameObject go = GameObject.Find("SocketIO"); + socket = go.GetComponent(); + + socket.On("unity", GetMesh); + + } + + + public void GetMesh(SocketIOEvent e) + { + + GameObject[] receivedMeshes = GameObject.FindGameObjectsWithTag("ReceivedMesh"); + + List meshDatas = e.data.GetField("meshes").list; + for (int n=0; n< meshDatas.Count; n++) + //foreach (JSONObject meshData in e.data.GetField("meshes").list) + { + JSONObject meshData = meshDatas[n]; + byte[] data = Convert.FromBase64String(meshData.str); + + var customMesh = ZeroFormatterSerializer.Deserialize(data); + + //print("verts: " + customMesh.vertices.Count); + + List vertices = new List(); + if (vertices != null) + { + for (int i = 0; i < customMesh.vertices.Count; i++) + { + vertices.Add(new Vector3(customMesh.vertices[i][0], customMesh.vertices[i][1], customMesh.vertices[i][2])); + } + } + + + List triangles = new List(); + if (customMesh.faces != null) + { + for (int i = 0; i < customMesh.faces.Count; i++) + { + if (customMesh.faces[i][0] == 0) + { + triangles.Add(customMesh.faces[i][1]); + triangles.Add(customMesh.faces[i][2]); + triangles.Add(customMesh.faces[i][3]); + } + else if (customMesh.faces[i][0] == 1) + { + triangles.Add(customMesh.faces[i][1]); + triangles.Add(customMesh.faces[i][2]); + triangles.Add(customMesh.faces[i][3]); + + triangles.Add(customMesh.faces[i][1]); + triangles.Add(customMesh.faces[i][3]); + triangles.Add(customMesh.faces[i][4]); + } + } + } + + List uvs = new List(); + if (customMesh.uvs != null) + { + for (int i = 0; i < customMesh.uvs.Count; i++) + { + uvs.Add(new Vector2(customMesh.uvs[i][0], customMesh.uvs[i][1])); + } + } + + List normals = new List(); + if (customMesh.normals != null) + { + for (int i = 0; i < customMesh.normals.Count; i++) + { + normals.Add(new Vector3(customMesh.normals[i][0], customMesh.normals[i][1], customMesh.normals[i][2])); + } + } + + MeshDraft meshDraft = new MeshDraft(); + meshDraft.vertices = vertices; + meshDraft.triangles = triangles; + meshDraft.uv = uvs; + meshDraft.normals = normals; + + if(n+1 >= receivedMeshes.Length) + { + GameObject receivedMeshInstance = (GameObject)Instantiate(receivedMeshPrefab); + receivedMeshInstance.GetComponent().mesh = meshDraft.ToMesh(); + + }else + { + receivedMeshes[n].GetComponent().mesh = meshDraft.ToMesh(); + } + + } + + if(receivedMeshes.Length > meshDatas.Count) + { + for(int i= meshDatas.Count; i< receivedMeshes.Length; i++) + { + Destroy(receivedMeshes[i]); + } + + Resources.UnloadUnusedAssets(); + } + + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/JSONObject/Editor/JSONChecker.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/JSONObject/Editor/JSONChecker.cs new file mode 100644 index 0000000..0b34878 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/JSONObject/Editor/JSONChecker.cs @@ -0,0 +1,51 @@ +//#define PERFTEST //For testing performance of parse/stringify. Turn on editor profiling to see how we're doing + +using UnityEngine; +using UnityEditor; + +public class JSONChecker : EditorWindow { + string JSON = @"{ + ""TestObject"": { + ""SomeText"": ""Blah"", + ""SomeObject"": { + ""SomeNumber"": 42, + ""SomeBool"": true, + ""SomeNull"": null + }, + + ""SomeEmptyObject"": { }, + ""SomeEmptyArray"": [ ], + ""EmbeddedObject"": ""{\""field\"":\""Value with \\\""escaped quotes\\\""\""}"" + } +}"; //dat string literal... + JSONObject j; + [MenuItem("Window/JSONChecker")] + static void Init() { + GetWindow(typeof(JSONChecker)); + } + void OnGUI() { + JSON = EditorGUILayout.TextArea(JSON); + GUI.enabled = !string.IsNullOrEmpty(JSON); + if(GUILayout.Button("Check JSON")) { +#if PERFTEST + Profiler.BeginSample("JSONParse"); + j = JSONObject.Create(JSON); + Profiler.EndSample(); + Profiler.BeginSample("JSONStringify"); + j.ToString(true); + Profiler.EndSample(); +#else + j = JSONObject.Create(JSON); +#endif + Debug.Log(j.ToString(true)); + } + if(j) { + //Debug.Log(System.GC.GetTotalMemory(false) + ""); + if(j.type == JSONObject.Type.NULL) + GUILayout.Label("JSON fail:\n" + j.ToString(true)); + else + GUILayout.Label("JSON success:\n" + j.ToString(true)); + + } + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/JSONObject/JSONObject.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/JSONObject/JSONObject.cs new file mode 100644 index 0000000..1611f8a --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/JSONObject/JSONObject.cs @@ -0,0 +1,1011 @@ +#define PRETTY //Comment out when you no longer need to read JSON to disable pretty Print system-wide +//Using doubles will cause errors in VectorTemplates.cs; Unity speaks floats +#define USEFLOAT //Use floats for numbers instead of doubles (enable if you're getting too many significant digits in string output) +//#define POOLING //Currently using a build setting for this one (also it's experimental) + +using System.Diagnostics; +using UnityEngine; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using Debug = UnityEngine.Debug; + +/* + * http://www.opensource.org/licenses/lgpl-2.1.php + * JSONObject class + * for use with Unity + * Copyright Matt Schoen 2010 - 2013 + */ + +public class JSONObject { +#if POOLING + const int MAX_POOL_SIZE = 10000; + public static Queue releaseQueue = new Queue(); +#endif + + const int MAX_DEPTH = 100; + const string INFINITY = "\"INFINITY\""; + const string NEGINFINITY = "\"NEGINFINITY\""; + const string NaN = "\"NaN\""; + static readonly char[] WHITESPACE = new[] { ' ', '\r', '\n', '\t' }; + public enum Type { NULL, STRING, NUMBER, OBJECT, ARRAY, BOOL, BAKED } + public bool isContainer { get { return (type == Type.ARRAY || type == Type.OBJECT); } } + public Type type = Type.NULL; + public int Count { + get { + if(list == null) + return -1; + return list.Count; + } + } + public List list; + public List keys; + public string str; +#if USEFLOAT + public float n; + public float f { + get { + return n; + } + } +#else + public double n; + public float f { + get { + return (float)n; + } + } +#endif + public bool b; + public delegate void AddJSONConents(JSONObject self); + + public static JSONObject nullJO { get { return Create(Type.NULL); } } //an empty, null object + public static JSONObject obj { get { return Create(Type.OBJECT); } } //an empty object + public static JSONObject arr { get { return Create(Type.ARRAY); } } //an empty array + + public JSONObject(Type t) { + type = t; + switch(t) { + case Type.ARRAY: + list = new List(); + break; + case Type.OBJECT: + list = new List(); + keys = new List(); + break; + } + } + public JSONObject(bool b) { + type = Type.BOOL; + this.b = b; + } +#if USEFLOAT + public JSONObject(float f) { + type = Type.NUMBER; + n = f; + } +#else + public JSONObject(double d) { + type = Type.NUMBER; + n = d; + } +#endif + public JSONObject(Dictionary dic) { + type = Type.OBJECT; + keys = new List(); + list = new List(); + //Not sure if it's worth removing the foreach here + foreach(KeyValuePair kvp in dic) { + keys.Add(kvp.Key); + list.Add(CreateStringObject(kvp.Value)); + } + } + public JSONObject(Dictionary dic) { + type = Type.OBJECT; + keys = new List(); + list = new List(); + //Not sure if it's worth removing the foreach here + foreach(KeyValuePair kvp in dic) { + keys.Add(kvp.Key); + list.Add(kvp.Value); + } + } + public JSONObject(AddJSONConents content) { + content.Invoke(this); + } + public JSONObject(JSONObject[] objs) { + type = Type.ARRAY; + list = new List(objs); + } + //Convenience function for creating a JSONObject containing a string. This is not part of the constructor so that malformed JSON data doesn't just turn into a string object + public static JSONObject StringObject(string val) { return CreateStringObject(val); } + public void Absorb(JSONObject obj) { + list.AddRange(obj.list); + keys.AddRange(obj.keys); + str = obj.str; + n = obj.n; + b = obj.b; + type = obj.type; + } + public static JSONObject Create() { +#if POOLING + JSONObject result = null; + while(result == null && releaseQueue.Count > 0) { + result = releaseQueue.Dequeue(); +#if DEV + //The following cases should NEVER HAPPEN (but they do...) + if(result == null) + Debug.Log("wtf " + releaseQueue.Count); + else if(result.list != null) + Debug.Log("wtflist " + result.list.Count); +#endif + } + if(result != null) + return result; +#endif + return new JSONObject(); + } + public static JSONObject Create(Type t) { + JSONObject obj = Create(); + obj.type = t; + switch(t) { + case Type.ARRAY: + obj.list = new List(); + break; + case Type.OBJECT: + obj.list = new List(); + obj.keys = new List(); + break; + } + return obj; + } + public static JSONObject Create(bool val) { + JSONObject obj = Create(); + obj.type = Type.BOOL; + obj.b = val; + return obj; + } + public static JSONObject Create(float val) { + JSONObject obj = Create(); + obj.type = Type.NUMBER; + obj.n = val; + return obj; + } + public static JSONObject Create(int val) { + JSONObject obj = Create(); + obj.type = Type.NUMBER; + obj.n = val; + return obj; + } + public static JSONObject CreateStringObject(string val) { + JSONObject obj = Create(); + obj.type = Type.STRING; + obj.str = val; + return obj; + } + public static JSONObject CreateBakedObject(string val) { + JSONObject bakedObject = Create(); + bakedObject.type = Type.BAKED; + bakedObject.str = val; + return bakedObject; + } + + + + /// + /// Create a JSONObject by parsing string data + /// + /// The string to be parsed + /// The maximum depth for the parser to search. Set this to to 1 for the first level, + /// 2 for the first 2 levels, etc. It defaults to -2 because -1 is the depth value that is parsed (see below) + /// Whether to store levels beyond maxDepth in baked JSONObjects + /// Whether to be strict in the parsing. For example, non-strict parsing will successfully + /// parse "a string" into a string-type + /// + /// + + // Default parameters fix + public static JSONObject Create(string val) { return Create(val, -2, false, false); } + public static JSONObject Create(string val, int maxDepth) { return Create(val, maxDepth, false, false); } + public static JSONObject Create(string val, int maxDepth, bool storeExcessLevels) { return Create(val, maxDepth, storeExcessLevels, false); } + public static JSONObject Create(string val, int maxDepth, bool storeExcessLevels, bool strict) { + JSONObject obj = Create(); + obj.Parse(val, maxDepth, storeExcessLevels, strict); + return obj; + } + public static JSONObject Create(AddJSONConents content) { + JSONObject obj = Create(); + content.Invoke(obj); + return obj; + } + public static JSONObject Create(Dictionary dic) { + JSONObject obj = Create(); + obj.type = Type.OBJECT; + obj.keys = new List(); + obj.list = new List(); + //Not sure if it's worth removing the foreach here + foreach(KeyValuePair kvp in dic) { + obj.keys.Add(kvp.Key); + obj.list.Add(CreateStringObject(kvp.Value)); + } + return obj; + } + public JSONObject() { } + #region PARSE + + // Default parameters fix + public JSONObject(string str){ Parse(str, -2, false, false); } + public JSONObject(string str, int maxDepth, bool storeExcessLevels, bool strict) { //create a new JSONObject from a string (this will also create any children, and parse the whole string) + Parse(str, maxDepth, storeExcessLevels, strict); + } + + // Default parameters fix + void Parse(string str) { Parse(str, -2, false, false); } + void Parse(string str, int maxDepth, bool storeExcessLevels, bool strict) { + if(!string.IsNullOrEmpty(str)) { + str = str.Trim(WHITESPACE); + if(strict) { + if(str[0] != '[' && str[0] != '{') { + type = Type.NULL; + Debug.LogWarning("Improper (strict) JSON formatting. First character must be [ or {"); + return; + } + } + if(str.Length > 0) { + if(string.Compare(str, "true", true) == 0) { + type = Type.BOOL; + b = true; + } else if(string.Compare(str, "false", true) == 0) { + type = Type.BOOL; + b = false; + } else if(string.Compare(str, "null", true) == 0) { + type = Type.NULL; +#if USEFLOAT + } else if(str == INFINITY) { + type = Type.NUMBER; + n = float.PositiveInfinity; + } else if(str == NEGINFINITY) { + type = Type.NUMBER; + n = float.NegativeInfinity; + } else if(str == NaN) { + type = Type.NUMBER; + n = float.NaN; +#else + } else if(str == INFINITY) { + type = Type.NUMBER; + n = double.PositiveInfinity; + } else if(str == NEGINFINITY) { + type = Type.NUMBER; + n = double.NegativeInfinity; + } else if(str == NaN) { + type = Type.NUMBER; + n = double.NaN; +#endif + } else if(str[0] == '"') { + type = Type.STRING; + this.str = str.Substring(1, str.Length - 2); + } else { + int tokenTmp = 1; + /* + * Checking for the following formatting (www.json.org) + * object - {"field1":value,"field2":value} + * array - [value,value,value] + * value - string - "string" + * - number - 0.0 + * - bool - true -or- false + * - null - null + */ + int offset = 0; + switch(str[offset]) { + case '{': + type = Type.OBJECT; + keys = new List(); + list = new List(); + break; + case '[': + type = Type.ARRAY; + list = new List(); + break; + default: + try { +#if USEFLOAT + n = System.Convert.ToSingle(str); +#else + n = System.Convert.ToDouble(str); +#endif + type = Type.NUMBER; + } catch(System.FormatException) { + type = Type.NULL; + Debug.LogWarning("improper JSON formatting:" + str); + } + return; + } + string propName = ""; + bool openQuote = false; + bool inProp = false; + int depth = 0; + while(++offset < str.Length) { + if(System.Array.IndexOf(WHITESPACE, str[offset]) > -1) + continue; + if(str[offset] == '\\') { + offset += 1; + continue; + } + if(str[offset] == '"') { + if(openQuote) { + if(!inProp && depth == 0 && type == Type.OBJECT) + propName = str.Substring(tokenTmp + 1, offset - tokenTmp - 1); + openQuote = false; + } else { + if(depth == 0 && type == Type.OBJECT) + tokenTmp = offset; + openQuote = true; + } + } + if(openQuote) + continue; + if(type == Type.OBJECT && depth == 0) { + if(str[offset] == ':') { + tokenTmp = offset + 1; + inProp = true; + } + } + + if(str[offset] == '[' || str[offset] == '{') { + depth++; + } else if(str[offset] == ']' || str[offset] == '}') { + depth--; + } + //if (encounter a ',' at top level) || a closing ]/} + if((str[offset] == ',' && depth == 0) || depth < 0) { + inProp = false; + string inner = str.Substring(tokenTmp, offset - tokenTmp).Trim(WHITESPACE); + if(inner.Length > 0) { + if(type == Type.OBJECT) + keys.Add(propName); + if(maxDepth != -1) //maxDepth of -1 is the end of the line + list.Add(Create(inner, (maxDepth < -1) ? -2 : maxDepth - 1)); + else if(storeExcessLevels) + list.Add(CreateBakedObject(inner)); + + } + tokenTmp = offset + 1; + } + } + } + } else type = Type.NULL; + } else type = Type.NULL; //If the string is missing, this is a null + //Profiler.EndSample(); + } + #endregion + public bool IsNumber { get { return type == Type.NUMBER; } } + public bool IsNull { get { return type == Type.NULL; } } + public bool IsString { get { return type == Type.STRING; } } + public bool IsBool { get { return type == Type.BOOL; } } + public bool IsArray { get { return type == Type.ARRAY; } } + public bool IsObject { get { return type == Type.OBJECT; } } + public void Add(bool val) { + Add(Create(val)); + } + public void Add(float val) { + Add(Create(val)); + } + public void Add(int val) { + Add(Create(val)); + } + public void Add(string str) { + Add(CreateStringObject(str)); + } + public void Add(AddJSONConents content) { + Add(Create(content)); + } + public void Add(JSONObject obj) { + if(obj) { //Don't do anything if the object is null + if(type != Type.ARRAY) { + type = Type.ARRAY; //Congratulations, son, you're an ARRAY now + if(list == null) + list = new List(); + } + list.Add(obj); + } + } + public void AddField(string name, bool val) { + AddField(name, Create(val)); + } + public void AddField(string name, float val) { + AddField(name, Create(val)); + } + public void AddField(string name, int val) { + AddField(name, Create(val)); + } + public void AddField(string name, AddJSONConents content) { + AddField(name, Create(content)); + } + public void AddField(string name, string val) { + AddField(name, CreateStringObject(val)); + } + public void AddField(string name, JSONObject obj) { + if(obj) { //Don't do anything if the object is null + if(type != Type.OBJECT) { + if(keys == null) + keys = new List(); + if(type == Type.ARRAY) { + for(int i = 0; i < list.Count; i++) + keys.Add(i + ""); + } else + if(list == null) + list = new List(); + type = Type.OBJECT; //Congratulations, son, you're an OBJECT now + } + keys.Add(name); + list.Add(obj); + } + } + public void SetField(string name, bool val) { SetField(name, Create(val)); } + public void SetField(string name, float val) { SetField(name, Create(val)); } + public void SetField(string name, int val) { SetField(name, Create(val)); } + public void SetField(string name, JSONObject obj) { + if(HasField(name)) { + list.Remove(this[name]); + keys.Remove(name); + } + AddField(name, obj); + } + public void RemoveField(string name) { + if(keys.IndexOf(name) > -1) { + list.RemoveAt(keys.IndexOf(name)); + keys.Remove(name); + } + } + public delegate void FieldNotFound(string name); + public delegate void GetFieldResponse(JSONObject obj); + + // Default parameters fix + public void GetField(ref bool field, string name) { GetField(ref field, name, null); } + public void GetField(ref bool field, string name, FieldNotFound fail) { + if(type == Type.OBJECT) { + int index = keys.IndexOf(name); + if(index >= 0) { + field = list[index].b; + return; + } + } + if(fail != null) fail.Invoke(name); + } +#if USEFLOAT + // Default parameters fix + public void GetField(ref float field, string name) { GetField(ref field, name, null); } + public void GetField(ref float field, string name, FieldNotFound fail) { +#else + // Default parameters fix + public void GetField(ref double field, string name) { GetField(ref field, name, null); } + public void GetField(ref double field, string name, FieldNotFound fail) { +#endif + if(type == Type.OBJECT) { + int index = keys.IndexOf(name); + if(index >= 0) { + field = list[index].n; + return; + } + } + if(fail != null) fail.Invoke(name); + } + + // Default parameters fix + public void GetField(ref int field, string name) { GetField(ref field, name, null); } + public void GetField(ref int field, string name, FieldNotFound fail) { + if(type == Type.OBJECT) { + int index = keys.IndexOf(name); + if(index >= 0) { + field = (int)list[index].n; + return; + } + } + if(fail != null) fail.Invoke(name); + } + + // Default parameters fix + public void GetField(ref uint field, string name) { GetField(ref field, name, null); } + public void GetField(ref uint field, string name, FieldNotFound fail) { + if(type == Type.OBJECT) { + int index = keys.IndexOf(name); + if(index >= 0) { + field = (uint)list[index].n; + return; + } + } + if(fail != null) fail.Invoke(name); + } + + // Default parameters fix + public void GetField(ref string field, string name) { GetField(ref field, name, null); } + public void GetField(ref string field, string name, FieldNotFound fail) { + if(type == Type.OBJECT) { + int index = keys.IndexOf(name); + if(index >= 0) { + field = list[index].str; + return; + } + } + if(fail != null) fail.Invoke(name); + } + + // Default parameters fix + public void GetField(string name, GetFieldResponse response) { GetField(name, response, null); } + public void GetField(string name, GetFieldResponse response, FieldNotFound fail) { + if(response != null && type == Type.OBJECT) { + int index = keys.IndexOf(name); + if(index >= 0) { + response.Invoke(list[index]); + return; + } + } + if(fail != null) fail.Invoke(name); + } + public JSONObject GetField(string name) { + if(type == Type.OBJECT) + for(int i = 0; i < keys.Count; i++) + if(keys[i] == name) + return list[i]; + return null; + } + public bool HasFields(string[] names) { + for(int i = 0; i < names.Length; i++) + if(!keys.Contains(names[i])) + return false; + return true; + } + public bool HasField(string name) { + if(type == Type.OBJECT) + for(int i = 0; i < keys.Count; i++) + if(keys[i] == name) + return true; + return false; + } + public void Clear() { + type = Type.NULL; + if(list != null) + list.Clear(); + if(keys != null) + keys.Clear(); + str = ""; + n = 0; + b = false; + } + /// + /// Copy a JSONObject. This could probably work better + /// + /// + public JSONObject Copy() { + return Create(Print()); + } + /* + * The Merge function is experimental. Use at your own risk. + */ + public void Merge(JSONObject obj) { + MergeRecur(this, obj); + } + /// + /// Merge object right into left recursively + /// + /// The left (base) object + /// The right (new) object + static void MergeRecur(JSONObject left, JSONObject right) { + if(left.type == Type.NULL) + left.Absorb(right); + else if(left.type == Type.OBJECT && right.type == Type.OBJECT) { + for(int i = 0; i < right.list.Count; i++) { + string key = right.keys[i]; + if(right[i].isContainer) { + if(left.HasField(key)) + MergeRecur(left[key], right[i]); + else + left.AddField(key, right[i]); + } else { + if(left.HasField(key)) + left.SetField(key, right[i]); + else + left.AddField(key, right[i]); + } + } + } else if(left.type == Type.ARRAY && right.type == Type.ARRAY) { + if(right.Count > left.Count) { + Debug.LogError("Cannot merge arrays when right object has more elements"); + return; + } + for(int i = 0; i < right.list.Count; i++) { + if(left[i].type == right[i].type) { //Only overwrite with the same type + if(left[i].isContainer) + MergeRecur(left[i], right[i]); + else { + left[i] = right[i]; + } + } + } + } + } + public void Bake() { + if(type != Type.BAKED) { + str = Print(); + type = Type.BAKED; + } + } + public IEnumerable BakeAsync() { + if(type != Type.BAKED) { + foreach(string s in PrintAsync()) { + if(s == null) + yield return s; + else { + str = s; + } + } + type = Type.BAKED; + } + } +#pragma warning disable 219 + // Default parameters fix + public string Print() { return Print(false); } + public string Print(bool pretty) { + StringBuilder builder = new StringBuilder(); + Stringify(0, builder, pretty); + return builder.ToString(); + } + // Default parameters fix + public IEnumerable PrintAsync() { return PrintAsync(false); } + public IEnumerable PrintAsync(bool pretty) { + StringBuilder builder = new StringBuilder(); + printWatch.Reset(); + printWatch.Start(); + foreach(IEnumerable e in StringifyAsync(0, builder, pretty)) { + yield return null; + } + yield return builder.ToString(); + } +#pragma warning restore 219 + #region STRINGIFY + const float maxFrameTime = 0.008f; + static readonly Stopwatch printWatch = new Stopwatch(); + + // Default parameters fix + IEnumerable StringifyAsync(int depth, StringBuilder builder) { return StringifyAsync(depth, builder, false); } + IEnumerable StringifyAsync(int depth, StringBuilder builder, bool pretty) { //Convert the JSONObject into a string + //Profiler.BeginSample("JSONprint"); + if(depth++ > MAX_DEPTH) { + Debug.Log("reached max depth!"); + yield break; + } + if(printWatch.Elapsed.TotalSeconds > maxFrameTime) { + printWatch.Reset(); + yield return null; + printWatch.Start(); + } + switch(type) { + case Type.BAKED: + builder.Append(str); + break; + case Type.STRING: + builder.AppendFormat("\"{0}\"", str); + break; + case Type.NUMBER: +#if USEFLOAT + if(float.IsInfinity(n)) + builder.Append(INFINITY); + else if(float.IsNegativeInfinity(n)) + builder.Append(NEGINFINITY); + else if(float.IsNaN(n)) + builder.Append(NaN); +#else + if(double.IsInfinity(n)) + builder.Append(INFINITY); + else if(double.IsNegativeInfinity(n)) + builder.Append(NEGINFINITY); + else if(double.IsNaN(n)) + builder.Append(NaN); +#endif + else + builder.Append(n.ToString()); + break; + case Type.OBJECT: + builder.Append("{"); + if(list.Count > 0) { +#if(PRETTY) //for a bit more readability, comment the define above to disable system-wide + if(pretty) + builder.Append("\n"); +#endif + for(int i = 0; i < list.Count; i++) { + string key = keys[i]; + JSONObject obj = list[i]; + if(obj) { +#if(PRETTY) + if(pretty) + for(int j = 0; j < depth; j++) + builder.Append("\t"); //for a bit more readability +#endif + builder.AppendFormat("\"{0}\":", key); + foreach(IEnumerable e in obj.StringifyAsync(depth, builder, pretty)) + yield return e; + builder.Append(","); +#if(PRETTY) + if(pretty) + builder.Append("\n"); +#endif + } + } +#if(PRETTY) + if(pretty) + builder.Length -= 2; + else +#endif + builder.Length--; + } +#if(PRETTY) + if(pretty && list.Count > 0) { + builder.Append("\n"); + for(int j = 0; j < depth - 1; j++) + builder.Append("\t"); //for a bit more readability + } +#endif + builder.Append("}"); + break; + case Type.ARRAY: + builder.Append("["); + if(list.Count > 0) { +#if(PRETTY) + if(pretty) + builder.Append("\n"); //for a bit more readability +#endif + for(int i = 0; i < list.Count; i++) { + if(list[i]) { +#if(PRETTY) + if(pretty) + for(int j = 0; j < depth; j++) + builder.Append("\t"); //for a bit more readability +#endif + foreach(IEnumerable e in list[i].StringifyAsync(depth, builder, pretty)) + yield return e; + builder.Append(","); +#if(PRETTY) + if(pretty) + builder.Append("\n"); //for a bit more readability +#endif + } + } +#if(PRETTY) + if(pretty) + builder.Length -= 2; + else +#endif + builder.Length--; + } +#if(PRETTY) + if(pretty && list.Count > 0) { + builder.Append("\n"); + for(int j = 0; j < depth - 1; j++) + builder.Append("\t"); //for a bit more readability + } +#endif + builder.Append("]"); + break; + case Type.BOOL: + if(b) + builder.Append("true"); + else + builder.Append("false"); + break; + case Type.NULL: + builder.Append("null"); + break; + } + //Profiler.EndSample(); + } + //TODO: Refactor Stringify functions to share core logic + /* + * I know, I know, this is really bad form. It turns out that there is a + * significant amount of garbage created when calling as a coroutine, so this + * method is duplicated. Hopefully there won't be too many future changes, but + * I would still like a more elegant way to optionaly yield + */ + // Default parameters fix + void Stringify(int depth, StringBuilder builder){ Stringify(depth, builder, false); } + void Stringify(int depth, StringBuilder builder, bool pretty) { //Convert the JSONObject into a string + //Profiler.BeginSample("JSONprint"); + if(depth++ > MAX_DEPTH) { + Debug.Log("reached max depth!"); + return; + } + switch(type) { + case Type.BAKED: + builder.Append(str); + break; + case Type.STRING: + builder.AppendFormat("\"{0}\"", str); + break; + case Type.NUMBER: +#if USEFLOAT + if(float.IsInfinity(n)) + builder.Append(INFINITY); + else if(float.IsNegativeInfinity(n)) + builder.Append(NEGINFINITY); + else if(float.IsNaN(n)) + builder.Append(NaN); +#else + if(double.IsInfinity(n)) + builder.Append(INFINITY); + else if(double.IsNegativeInfinity(n)) + builder.Append(NEGINFINITY); + else if(double.IsNaN(n)) + builder.Append(NaN); +#endif + else + builder.Append(n.ToString()); + break; + case Type.OBJECT: + builder.Append("{"); + if(list.Count > 0) { +#if(PRETTY) //for a bit more readability, comment the define above to disable system-wide + if(pretty) + builder.Append("\n"); +#endif + for(int i = 0; i < list.Count; i++) { + string key = keys[i]; + JSONObject obj = list[i]; + if(obj) { +#if(PRETTY) + if(pretty) + for(int j = 0; j < depth; j++) + builder.Append("\t"); //for a bit more readability +#endif + builder.AppendFormat("\"{0}\":", key); + obj.Stringify(depth, builder, pretty); + builder.Append(","); +#if(PRETTY) + if(pretty) + builder.Append("\n"); +#endif + } + } +#if(PRETTY) + if(pretty) + builder.Length -= 2; + else +#endif + builder.Length--; + } +#if(PRETTY) + if(pretty && list.Count > 0) { + builder.Append("\n"); + for(int j = 0; j < depth - 1; j++) + builder.Append("\t"); //for a bit more readability + } +#endif + builder.Append("}"); + break; + case Type.ARRAY: + builder.Append("["); + if(list.Count > 0) { +#if(PRETTY) + if(pretty) + builder.Append("\n"); //for a bit more readability +#endif + for(int i = 0; i < list.Count; i++) { + if(list[i]) { +#if(PRETTY) + if(pretty) + for(int j = 0; j < depth; j++) + builder.Append("\t"); //for a bit more readability +#endif + list[i].Stringify(depth, builder, pretty); + builder.Append(","); +#if(PRETTY) + if(pretty) + builder.Append("\n"); //for a bit more readability +#endif + } + } +#if(PRETTY) + if(pretty) + builder.Length -= 2; + else +#endif + builder.Length--; + } +#if(PRETTY) + if(pretty && list.Count > 0) { + builder.Append("\n"); + for(int j = 0; j < depth - 1; j++) + builder.Append("\t"); //for a bit more readability + } +#endif + builder.Append("]"); + break; + case Type.BOOL: + if(b) + builder.Append("true"); + else + builder.Append("false"); + break; + case Type.NULL: + builder.Append("null"); + break; + } + //Profiler.EndSample(); + } + #endregion + public static implicit operator WWWForm(JSONObject obj) { + WWWForm form = new WWWForm(); + for(int i = 0; i < obj.list.Count; i++) { + string key = i + ""; + if(obj.type == Type.OBJECT) + key = obj.keys[i]; + string val = obj.list[i].ToString(); + if(obj.list[i].type == Type.STRING) + val = val.Replace("\"", ""); + form.AddField(key, val); + } + return form; + } + public JSONObject this[int index] { + get { + if(list.Count > index) return list[index]; + return null; + } + set { + if(list.Count > index) + list[index] = value; + } + } + public JSONObject this[string index] { + get { + return GetField(index); + } + set { + SetField(index, value); + } + } + public override string ToString() { + return Print(); + } + public string ToString(bool pretty) { + return Print(pretty); + } + public Dictionary ToDictionary() { + if(type == Type.OBJECT) { + Dictionary result = new Dictionary(); + for(int i = 0; i < list.Count; i++) { + JSONObject val = list[i]; + switch(val.type) { + case Type.STRING: result.Add(keys[i], val.str); break; + case Type.NUMBER: result.Add(keys[i], val.n + ""); break; + case Type.BOOL: result.Add(keys[i], val.b + ""); break; + default: Debug.LogWarning("Omitting object: " + keys[i] + " in dictionary conversion"); break; + } + } + return result; + } + Debug.LogWarning("Tried to turn non-Object JSONObject into a dictionary"); + return null; + } + public static implicit operator bool(JSONObject o) { + return o != null; + } +#if POOLING + static bool pool = true; + public static void ClearPool() { + pool = false; + releaseQueue.Clear(); + pool = true; + } + + ~JSONObject() { + if(pool && releaseQueue.Count < MAX_POOL_SIZE) { + type = Type.NULL; + list = null; + keys = null; + str = ""; + n = 0; + b = false; + releaseQueue.Enqueue(this); + } + } +#endif +} \ No newline at end of file diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/JSONObject/JSONTemplates.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/JSONObject/JSONTemplates.cs new file mode 100644 index 0000000..749107d --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/JSONObject/JSONTemplates.cs @@ -0,0 +1,88 @@ +using UnityEngine; +using System.Collections.Generic; +using System.Reflection; + +/* + * http://www.opensource.org/licenses/lgpl-2.1.php + * JSONTemplates class + * for use with Unity + * Copyright Matt Schoen 2010 + */ + +public static partial class JSONTemplates { + static readonly HashSet touched = new HashSet(); + + public static JSONObject TOJSON(object obj) { //For a generic guess + if(touched.Add(obj)) { + JSONObject result = JSONObject.obj; + //Fields + FieldInfo[] fieldinfo = obj.GetType().GetFields(); + foreach(FieldInfo fi in fieldinfo) { + JSONObject val = JSONObject.nullJO; + if(!fi.GetValue(obj).Equals(null)) { + MethodInfo info = typeof(JSONTemplates).GetMethod("From" + fi.FieldType.Name); + if(info != null) { + object[] parms = new object[1]; + parms[0] = fi.GetValue(obj); + val = (JSONObject)info.Invoke(null, parms); + } else if(fi.FieldType == typeof(string)) + val = JSONObject.CreateStringObject(fi.GetValue(obj).ToString()); + else + val = JSONObject.Create(fi.GetValue(obj).ToString()); + } + if(val) { + if(val.type != JSONObject.Type.NULL) + result.AddField(fi.Name, val); + else Debug.LogWarning("Null for this non-null object, property " + fi.Name + " of class " + obj.GetType().Name + ". Object type is " + fi.FieldType.Name); + } + } + //Properties + PropertyInfo[] propertyInfo = obj.GetType().GetProperties(); + foreach(PropertyInfo pi in propertyInfo) { + //This section should mirror part of AssetFactory.AddScripts() + JSONObject val = JSONObject.nullJO; + if(!pi.GetValue(obj, null).Equals(null)) { + MethodInfo info = typeof(JSONTemplates).GetMethod("From" + pi.PropertyType.Name); + if(info != null) { + object[] parms = new object[1]; + parms[0] = pi.GetValue(obj, null); + val = (JSONObject)info.Invoke(null, parms); + } else if(pi.PropertyType == typeof(string)) + val = JSONObject.CreateStringObject(pi.GetValue(obj, null).ToString()); + else + val = JSONObject.Create(pi.GetValue(obj, null).ToString()); + } + if(val) { + if(val.type != JSONObject.Type.NULL) + result.AddField(pi.Name, val); + else Debug.LogWarning("Null for this non-null object, property " + pi.Name + " of class " + obj.GetType().Name + ". Object type is " + pi.PropertyType.Name); + } + } + return result; + } + Debug.LogWarning("trying to save the same data twice"); + return JSONObject.nullJO; + } +} + +/* + * Some helpful code templates for the JSON class + * + * LOOP THROUGH OBJECT +for(int i = 0; i < obj.Count; i++){ + if(obj.keys[i] != null){ + switch((string)obj.keys[i]){ + case "key1": + do stuff with (JSONObject)obj.list[i]; + break; + case "key2": + do stuff with (JSONObject)obj.list[i]; + break; + } + } +} + * + * LOOP THROUGH ARRAY +foreach(JSONObject ob in obj.list) + do stuff with ob; + */ diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/JSONObject/VectorTemplates.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/JSONObject/VectorTemplates.cs new file mode 100644 index 0000000..f0c1328 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/JSONObject/VectorTemplates.cs @@ -0,0 +1,237 @@ +using UnityEngine; + +public static partial class JSONTemplates { + + /* + * Vector2 + */ + public static Vector2 ToVector2(JSONObject obj) { + float x = obj["x"] ? obj["x"].f : 0; + float y = obj["y"] ? obj["y"].f : 0; + return new Vector2(x, y); + } + public static JSONObject FromVector2(Vector2 v) { + JSONObject vdata = JSONObject.obj; + if(v.x != 0) vdata.AddField("x", v.x); + if(v.y != 0) vdata.AddField("y", v.y); + return vdata; + } + /* + * Vector3 + */ + public static JSONObject FromVector3(Vector3 v) { + JSONObject vdata = JSONObject.obj; + if(v.x != 0) vdata.AddField("x", v.x); + if(v.y != 0) vdata.AddField("y", v.y); + if(v.z != 0) vdata.AddField("z", v.z); + return vdata; + } + public static Vector3 ToVector3(JSONObject obj) { + float x = obj["x"] ? obj["x"].f : 0; + float y = obj["y"] ? obj["y"].f : 0; + float z = obj["z"] ? obj["z"].f : 0; + return new Vector3(x, y, z); + } + /* + * Vector4 + */ + public static JSONObject FromVector4(Vector4 v) { + JSONObject vdata = JSONObject.obj; + if(v.x != 0) vdata.AddField("x", v.x); + if(v.y != 0) vdata.AddField("y", v.y); + if(v.z != 0) vdata.AddField("z", v.z); + if(v.w != 0) vdata.AddField("w", v.w); + return vdata; + } + public static Vector4 ToVector4(JSONObject obj) { + float x = obj["x"] ? obj["x"].f : 0; + float y = obj["y"] ? obj["y"].f : 0; + float z = obj["z"] ? obj["z"].f : 0; + float w = obj["w"] ? obj["w"].f : 0; + return new Vector4(x, y, z, w); + } + /* + * Matrix4x4 + */ + public static JSONObject FromMatrix4x4(Matrix4x4 m) { + JSONObject mdata = JSONObject.obj; + if(m.m00 != 0) mdata.AddField("m00", m.m00); + if(m.m01 != 0) mdata.AddField("m01", m.m01); + if(m.m02 != 0) mdata.AddField("m02", m.m02); + if(m.m03 != 0) mdata.AddField("m03", m.m03); + if(m.m10 != 0) mdata.AddField("m10", m.m10); + if(m.m11 != 0) mdata.AddField("m11", m.m11); + if(m.m12 != 0) mdata.AddField("m12", m.m12); + if(m.m13 != 0) mdata.AddField("m13", m.m13); + if(m.m20 != 0) mdata.AddField("m20", m.m20); + if(m.m21 != 0) mdata.AddField("m21", m.m21); + if(m.m22 != 0) mdata.AddField("m22", m.m22); + if(m.m23 != 0) mdata.AddField("m23", m.m23); + if(m.m30 != 0) mdata.AddField("m30", m.m30); + if(m.m31 != 0) mdata.AddField("m31", m.m31); + if(m.m32 != 0) mdata.AddField("m32", m.m32); + if(m.m33 != 0) mdata.AddField("m33", m.m33); + return mdata; + } + public static Matrix4x4 ToMatrix4x4(JSONObject obj) { + Matrix4x4 result = new Matrix4x4(); + if(obj["m00"]) result.m00 = obj["m00"].f; + if(obj["m01"]) result.m01 = obj["m01"].f; + if(obj["m02"]) result.m02 = obj["m02"].f; + if(obj["m03"]) result.m03 = obj["m03"].f; + if(obj["m10"]) result.m10 = obj["m10"].f; + if(obj["m11"]) result.m11 = obj["m11"].f; + if(obj["m12"]) result.m12 = obj["m12"].f; + if(obj["m13"]) result.m13 = obj["m13"].f; + if(obj["m20"]) result.m20 = obj["m20"].f; + if(obj["m21"]) result.m21 = obj["m21"].f; + if(obj["m22"]) result.m22 = obj["m22"].f; + if(obj["m23"]) result.m23 = obj["m23"].f; + if(obj["m30"]) result.m30 = obj["m30"].f; + if(obj["m31"]) result.m31 = obj["m31"].f; + if(obj["m32"]) result.m32 = obj["m32"].f; + if(obj["m33"]) result.m33 = obj["m33"].f; + return result; + } + /* + * Quaternion + */ + public static JSONObject FromQuaternion(Quaternion q) { + JSONObject qdata = JSONObject.obj; + if(q.w != 0) qdata.AddField("w", q.w); + if(q.x != 0) qdata.AddField("x", q.x); + if(q.y != 0) qdata.AddField("y", q.y); + if(q.z != 0) qdata.AddField("z", q.z); + return qdata; + } + public static Quaternion ToQuaternion(JSONObject obj) { + float x = obj["x"] ? obj["x"].f : 0; + float y = obj["y"] ? obj["y"].f : 0; + float z = obj["z"] ? obj["z"].f : 0; + float w = obj["w"] ? obj["w"].f : 0; + return new Quaternion(x, y, z, w); + } + /* + * Color + */ + public static JSONObject FromColor(Color c) { + JSONObject cdata = JSONObject.obj; + if(c.r != 0) cdata.AddField("r", c.r); + if(c.g != 0) cdata.AddField("g", c.g); + if(c.b != 0) cdata.AddField("b", c.b); + if(c.a != 0) cdata.AddField("a", c.a); + return cdata; + } + public static Color ToColor(JSONObject obj) { + Color c = new Color(); + for(int i = 0; i < obj.Count; i++) { + switch(obj.keys[i]) { + case "r": c.r = obj[i].f; break; + case "g": c.g = obj[i].f; break; + case "b": c.b = obj[i].f; break; + case "a": c.a = obj[i].f; break; + } + } + return c; + } + /* + * Layer Mask + */ + public static JSONObject FromLayerMask(LayerMask l) { + JSONObject result = JSONObject.obj; + result.AddField("value", l.value); + return result; + } + public static LayerMask ToLayerMask(JSONObject obj) { + LayerMask l = new LayerMask {value = (int)obj["value"].n}; + return l; + } + public static JSONObject FromRect(Rect r) { + JSONObject result = JSONObject.obj; + if(r.x != 0) result.AddField("x", r.x); + if(r.y != 0) result.AddField("y", r.y); + if(r.height != 0) result.AddField("height", r.height); + if(r.width != 0) result.AddField("width", r.width); + return result; + } + public static Rect ToRect(JSONObject obj) { + Rect r = new Rect(); + for(int i = 0; i < obj.Count; i++) { + switch(obj.keys[i]) { + case "x": r.x = obj[i].f; break; + case "y": r.y = obj[i].f; break; + case "height": r.height = obj[i].f; break; + case "width": r.width = obj[i].f; break; + } + } + return r; + } + public static JSONObject FromRectOffset(RectOffset r) { + JSONObject result = JSONObject.obj; + if(r.bottom != 0) result.AddField("bottom", r.bottom); + if(r.left != 0) result.AddField("left", r.left); + if(r.right != 0) result.AddField("right", r.right); + if(r.top != 0) result.AddField("top", r.top); + return result; + } + public static RectOffset ToRectOffset(JSONObject obj) { + RectOffset r = new RectOffset(); + for(int i = 0; i < obj.Count; i++) { + switch(obj.keys[i]) { + case "bottom": r.bottom = (int)obj[i].n; break; + case "left": r.left = (int)obj[i].n; break; + case "right": r.right = (int)obj[i].n; break; + case "top": r.top = (int)obj[i].n; break; + } + } + return r; + } + + public static AnimationCurve ToAnimationCurve(JSONObject obj){ + AnimationCurve a = new AnimationCurve(); + if(obj.HasField("keys")){ + JSONObject keys = obj.GetField("keys"); + for(int i =0; i < keys.list.Count;i++){ + a.AddKey(ToKeyframe(keys[i])); + } + } + if(obj.HasField("preWrapMode")) + a.preWrapMode = (WrapMode)((int)obj.GetField("preWrapMode").n); + if(obj.HasField("postWrapMode")) + a.postWrapMode = (WrapMode)((int)obj.GetField("postWrapMode").n); + return a; + } + + public static JSONObject FromAnimationCurve(AnimationCurve a){ + JSONObject result = JSONObject.obj; + result.AddField("preWrapMode", a.preWrapMode.ToString()); + result.AddField("postWrapMode", a.postWrapMode.ToString()); + if(a.keys.Length > 0){ + JSONObject keysJSON = JSONObject.Create(); + for(int i =0; i < a.keys.Length;i++){ + keysJSON.Add(FromKeyframe(a.keys[i])); + } + result.AddField("keys", keysJSON); + } + return result; + } + + public static Keyframe ToKeyframe(JSONObject obj){ + Keyframe k = new Keyframe(obj.HasField("time")? obj.GetField("time").n : 0, obj.HasField("value")? obj.GetField("value").n : 0); + if(obj.HasField("inTangent")) k.inTangent = obj.GetField("inTangent").n; + if(obj.HasField("outTangent")) k.outTangent = obj.GetField("outTangent").n; + if(obj.HasField("tangentMode")) k.tangentMode = (int)obj.GetField("tangentMode").n; + + return k; + } + public static JSONObject FromKeyframe(Keyframe k){ + JSONObject result = JSONObject.obj; + if(k.inTangent != 0) result.AddField("inTangent", k.inTangent); + if(k.outTangent != 0) result.AddField("outTangent", k.outTangent); + if(k.tangentMode != 0) result.AddField("tangentMode", k.tangentMode); + if(k.time != 0) result.AddField("time", k.time); + if(k.value != 0) result.AddField("value", k.value); + return result; + } + +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/JSONObject/readme.txt b/Examples/MeshReceivingUnityClient/Assets/SocketIO/JSONObject/readme.txt new file mode 100644 index 0000000..b7044d4 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/JSONObject/readme.txt @@ -0,0 +1,188 @@ +==Author== +[mailto:schoen@defectivestudios.com Matt Schoen] of [http://www.defectivestudios.com Defective Studios] + +==Download== +[[Media:JSONObject.zip|Download JSONObject.zip]] + += Intro = +I came across the need to send structured data to and from a server on one of my projects, and figured it would be worth my while to use JSON. When I looked into the issue, I tried a few of the C# implementations listed on [http://json.org json.org], but found them to be too complicated to work with and expand upon. So, I've written a very simple JSONObject class, which can be generically used to encode/decode data into a simple container. This page assumes that you know what JSON is, and how it works. It's rather simple, just go to json.org for a visual description of the encoding format. + +As an aside, this class is pretty central to the [[AssetCloud]] content management system, from Defective Studios. + +Update: The code has been updated to version 1.4 to incorporate user-submitted patches and bug reports. This fixes issues dealing with whitespace in the format, as well as empty arrays and objects, and escaped quotes within strings. + += Usage = +Users should not have to modify the JSONObject class themselves, and must follow the very simple proceedures outlined below: + +Sample data (in JSON format): + +{ + "TestObject": { + "SomeText": "Blah", + "SomeObject": { + "SomeNumber": 42, + "SomeBool": true, + "SomeNull": null + }, + + "SomeEmptyObject": { }, + "SomeEmptyArray": [ ], + "EmbeddedObject": "{\"field\":\"Value with \\\"escaped quotes\\\"\"}" + } +} + += Features = + +*Decode JSON-formatted strings into a usable data structure +*Encode structured data into a JSON-formatted string +*Interoperable with Dictionary and WWWForm +*Optimized parse/stringify functions -- minimal (unavoidable) garbage creation +*Asynchronous stringify function for serializing lots of data without frame drops +*MaxDepth parsing will skip over nested data that you don't need +*Special (non-compliant) "Baked" object type can store stringified data within parsed objects +*Copy to new JSONObject +*Merge with another JSONObject (experimental) +*Random access (with [int] or [string]) +*ToString() returns JSON data with optional "pretty" flag to include newlines and tabs +*Switch between double and float for numeric storage depending on level of precision needed (and to ensure that numbers are parsed/stringified correctly) +*Supports Infinity and NaN values +*JSONTemplates static class provides serialization functions for common classes like Vector3, Matrix4x4 +*Object pool implementation (experimental) +*Handy JSONChecker window to test parsing on sample data + +It should be pretty obvious what this parser can and cannot do. If anyone reading this is a JSON buff (is there such a thing?) please feel free to expand and modify the parser to be more compliant. Currently I am using the .NET System.Convert namespace functions for parsing the data itself. It parses strings and numbers, which was all that I needed of it, but unless the formatting is supported by System.Convert, it may not incorporate all proper JSON strings. Also, having never written a JSON parser before, I don't doubt that I could improve the efficiency or correctness of the parser. It serves my purpose, and hopefully will help you with your project! Let me know if you make any improvements :) + +Also, you JSON buffs (really, who would admit to being a JSON buff...) might also notice from my feature list that this thing isn't exactly to specifications. Here is where it differs: +*"a string" is considered valid JSON. There is an optional "strict" parameter to the parser which will bomb out on such input, in case that matters to you. +*The "Baked" mode is totally made up. +*The "MaxDepth" parsing is totally made up. +*NaN and Infinity aren't officially supported by JSON ([http://stackoverflow.com/questions/1423081/json-left-out-infinity-and-nan-json-status-in-ecmascript read more] about this issue... I lol'd @ the first comment on the first answer) +*I have no idea about edge cases in my parsing strategy. I have been using this code for about 3 years now and have only had to modify the parser because other people's use cases (still valid JSON) didn't parse correctly. In my experience, anything that this code generates is parsed just fine. + +== Encoding == + +Encoding is something of a hard-coded process. This is because I have no idea what your data is! It would be great if this were some sort of interface for taking an entire class and encoding it's number/string fields, but it's not. I've come up with a few clever ways of using loops and/or recursive methods to cut down of the amount of code I have to write when I use this tool, but they're pretty project-specific. + +Note: This section used to be WRONG! And now it's OLD! Will update later... this will all still work, but there are now a couple of ways to skin this cat. + + +//Note: your data can only be numbers and strings. This is not a solution for object serialization or anything like that. +JSONObject j = new JSONObject(JSONObject.Type.OBJECT); +//number +j.AddField("field1", 0.5); +//string +j.AddField("field2", "sampletext"); +//array +JSONObject arr = new JSONObject(JSONObject.Type.ARRAY); +j.AddField("field3", arr); + +arr.Add(1); +arr.Add(2); +arr.Add(3); + +string encodedString = j.print(); + + +NEW! The constructor, Add, and AddField functions now support a nested delegate structure. This is useful if you need to create a nested JSONObject in a single line. For example: + +DoRequest(URL, new JSONObject(delegate(JSONObject request) { + request.AddField("sort", delegate(JSONObject sort) { + sort.AddField("_timestamp", "desc"); + }); + request.AddField("query", new JSONObject(delegate(JSONObject query) { + query.AddField("match_all", JSONObject.obj); + })); + request.AddField("fields", delegate(JSONObject fields) { + fields.Add("_timestamp"); + }); +}).ToString()); + + +== Decoding == +Decoding is much simpler on the input end, and again, what you do with the JSONObject will vary on a per-project basis. One of the more complicated way to extract the data is with a recursive function, as drafted below. Calling the constructor with a properly formatted JSON string will return the root object (or array) containing all of its children, in one neat reference! The data is in a public ArrayList called list, with a matching key list (called keys!) if the root is an Object. If that's confusing, take a glance over the following code and the print() method in the JSONOBject class. If there is an error in the JSON formatting (or if there's an error with my code!) the debug console will read "improper JSON formatting". + + + +string encodedString = "{\"field1\": 0.5,\"field2\": \"sampletext\",\"field3\": [1,2,3]}"; +JSONObject j = new JSONObject(encodedString); +accessData(j); +//access data (and print it) +void accessData(JSONObject obj){ + switch(obj.type){ + case JSONObject.Type.OBJECT: + for(int i = 0; i < obj.list.Count; i++){ + string key = (string)obj.keys[i]; + JSONObject j = (JSONObject)obj.list[i]; + Debug.Log(key); + accessData(j); + } + break; + case JSONObject.Type.ARRAY: + foreach(JSONObject j in obj.list){ + accessData(j); + } + break; + case JSONObject.Type.STRING: + Debug.Log(obj.str); + break; + case JSONObject.Type.NUMBER: + Debug.Log(obj.n); + break; + case JSONObject.Type.BOOL: + Debug.Log(obj.b); + break; + case JSONObject.Type.NULL: + Debug.Log("NULL"); + break; + + } +} + + +NEW! Decoding now also supports a delegate format which will automatically check if a field exists before processing the data, providing an optional parameter for an OnFieldNotFound response. For example: + +new JSONObject(data); +list.GetField("hits", delegate(JSONObject hits) { + hits.GetField("hits", delegate(JSONObject hits2) { + foreach (JSONObject gameSession in hits2.list) { + Debug.Log(gameSession); + } + }); +}, delegate(string name) { //"name" will be equal to the name of the missing field. In this case, "hits" + Debug.LogWarning("no game sessions"); +}); + + +===Not So New! (O(n)) Random access!=== +I've added a string and int [] index to the class, so you can now retrieve data as such (from above): + +JSONObject arr = obj["field3"]; +Debug.log(arr[2].n); //Should ouptut "3" + + +---- + +---Code omitted from readme--- + +=Change Log= +==v1.4== +Big update! +*Better GC performance. Enough of that garbage! +**Remaining culprits are internal garbage from StringBuilder.Append/AppendFormat, String.Substring, List.Add/GrowIfNeeded, Single.ToString +*Added asynchronous Stringify function for serializing large amounts of data at runtime without frame drops +*Added Baked type +*Added MaxDepth to parsing function +*Various cleanup refactors recommended by ReSharper + +==v1.3.2== +*Added support for NaN +*Added strict mode to fail on purpose for improper formatting. Right now this just means that if the parse string doesn't start with [ or {, it will print a warning and return a null JO. +*Changed infinity and NaN implementation to use float and double instead of Mathf +*Handles empty objects/arrays better +*Added a flag to print and ToString to turn on/off pretty print. The define on top is now an override to system-wide disable +==Earlier Versions== +I'll fill these in later... +[[Category:C Sharp]] +[[Category:Scripts]] +[[Category:Utility]] +[[Category:JSON]] diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/Prefabs/SocketIO.prefab b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Prefabs/SocketIO.prefab new file mode 100644 index 0000000..329df62 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Prefabs/SocketIO.prefab @@ -0,0 +1,56 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &100000 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 4 + m_Component: + - 4: {fileID: 400000} + - 114: {fileID: 11400000} + m_Layer: 0 + m_Name: SocketIO + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &400000 +Transform: + m_ObjectHideFlags: 1 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 100000} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 100000} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 251dc7db9857fae4e8c9293b45173db2, type: 3} + m_Name: + m_EditorClassIdentifier: + url: ws://localhost:4567/socket.io/?EIO=4&transport=websocket + autoConnect: 1 + recconectDelay: 5 +--- !u!1001 &100100000 +Prefab: + m_ObjectHideFlags: 1 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: [] + m_RemovedComponents: [] + m_ParentPrefab: {fileID: 0} + m_RootGameObject: {fileID: 100000} + m_IsPrefabParent: 1 + m_IsExploded: 1 diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scenes/MainScene.unity b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scenes/MainScene.unity new file mode 100644 index 0000000..bc89021 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scenes/MainScene.unity @@ -0,0 +1,286 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +SceneSettings: + m_ObjectHideFlags: 0 + m_PVSData: + m_PVSObjectsArray: [] + m_PVSPortalsArray: [] + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: .25 + backfaceThreshold: 100 +--- !u!104 &2 +RenderSettings: + m_Fog: 0 + m_FogColor: {r: .5, g: .5, b: .5, a: 1} + m_FogMode: 3 + m_FogDensity: .00999999978 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientLight: {r: .200000003, g: .200000003, b: .200000003, a: 1} + m_SkyboxMaterial: {fileID: 0} + m_HaloStrength: .5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 0} + m_ObjectHideFlags: 0 +--- !u!127 &3 +LevelGameManager: + m_ObjectHideFlags: 0 +--- !u!157 &4 +LightmapSettings: + m_ObjectHideFlags: 0 + m_LightProbes: {fileID: 0} + m_Lightmaps: [] + m_LightmapsMode: 1 + m_BakedColorSpace: 0 + m_UseDualLightmapsInForward: 0 + m_LightmapEditorSettings: + m_Resolution: 50 + m_LastUsedResolution: 0 + m_TextureWidth: 1024 + m_TextureHeight: 1024 + m_BounceBoost: 1 + m_BounceIntensity: 1 + m_SkyLightColor: {r: .860000014, g: .930000007, b: 1, a: 1} + m_SkyLightIntensity: 0 + m_Quality: 0 + m_Bounces: 1 + m_FinalGatherRays: 1000 + m_FinalGatherContrastThreshold: .0500000007 + m_FinalGatherGradientThreshold: 0 + m_FinalGatherInterpolationPoints: 15 + m_AOAmount: 0 + m_AOMaxDistance: .100000001 + m_AOContrast: 1 + m_LODSurfaceMappingDistance: 1 + m_Padding: 0 + m_TextureCompression: 0 + m_LockAtlas: 0 +--- !u!196 &5 +NavMeshSettings: + m_ObjectHideFlags: 0 + m_BuildSettings: + agentRadius: .5 + agentHeight: 2 + agentSlope: 45 + agentClimb: .400000006 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + accuratePlacement: 0 + minRegionArea: 2 + widthInaccuracy: 16.666666 + heightInaccuracy: 10 + m_NavMesh: {fileID: 0} +--- !u!1 &244749905 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 100000, guid: b30cc084d923f0645b55bba668610d48, type: 2} + m_PrefabInternal: {fileID: 1122655700} + serializedVersion: 4 + m_Component: + - 4: {fileID: 244749907} + - 114: {fileID: 244749906} + m_Layer: 0 + m_Name: SocketIO + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &244749906 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 11400000, guid: b30cc084d923f0645b55bba668610d48, + type: 2} + m_PrefabInternal: {fileID: 1122655700} + m_GameObject: {fileID: 244749905} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 251dc7db9857fae4e8c9293b45173db2, type: 3} + m_Name: + m_EditorClassIdentifier: + url: ws://localhost:4567/socket.io/?EIO=4&transport=websocket + autoConnect: 1 + recconectDelay: 5 +--- !u!4 &244749907 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 400000, guid: b30cc084d923f0645b55bba668610d48, type: 2} + m_PrefabInternal: {fileID: 1122655700} + m_GameObject: {fileID: 244749905} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 +--- !u!1001 &1122655700 +Prefab: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 400000, guid: b30cc084d923f0645b55bba668610d48, type: 2} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 400000, guid: b30cc084d923f0645b55bba668610d48, type: 2} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 400000, guid: b30cc084d923f0645b55bba668610d48, type: 2} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 400000, guid: b30cc084d923f0645b55bba668610d48, type: 2} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 400000, guid: b30cc084d923f0645b55bba668610d48, type: 2} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 400000, guid: b30cc084d923f0645b55bba668610d48, type: 2} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 400000, guid: b30cc084d923f0645b55bba668610d48, type: 2} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 400000, guid: b30cc084d923f0645b55bba668610d48, type: 2} + propertyPath: m_RootOrder + value: 2 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_ParentPrefab: {fileID: 100100000, guid: b30cc084d923f0645b55bba668610d48, type: 2} + m_RootGameObject: {fileID: 244749905} + m_IsPrefabParent: 0 + m_IsExploded: 1 +--- !u!1 &1727790172 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 4 + m_Component: + - 4: {fileID: 1727790174} + - 114: {fileID: 1727790173} + m_Layer: 0 + m_Name: TestSocket + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1727790173 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1727790172} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c3c41d927209c2049b714971a1271478, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!4 &1727790174 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1727790172} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 +--- !u!1 &2055889949 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 4 + m_Component: + - 4: {fileID: 2055889954} + - 20: {fileID: 2055889953} + - 92: {fileID: 2055889952} + - 124: {fileID: 2055889951} + - 81: {fileID: 2055889950} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &2055889950 +AudioListener: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2055889949} + m_Enabled: 1 +--- !u!124 &2055889951 +Behaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2055889949} + m_Enabled: 1 +--- !u!92 &2055889952 +Behaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2055889949} + m_Enabled: 1 +--- !u!20 &2055889953 +Camera: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2055889949} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: .192156866, g: .301960796, b: .474509805, a: .0196078438} + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: .300000012 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_HDR: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: .0219999999 +--- !u!4 &2055889954 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2055889949} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/Ack.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/Ack.cs new file mode 100644 index 0000000..f1b36f8 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/Ack.cs @@ -0,0 +1,58 @@ +#region License +/* + * Ack.cs + * + * The MIT License + * + * Copyright (c) 2014 Fabio Panettieri + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; + +namespace SocketIO +{ + public class Ack + { + public int packetId; + public DateTime time; + + private System.Action action; + + public Ack(int packetId, System.Action action) + { + this.packetId = packetId; + this.time = DateTime.Now; + this.action = action; + } + + public void Invoke(JSONObject ev) + { + action.Invoke(ev); + } + + public override string ToString() + { + return string.Format("[Ack: packetId={0}, time={1}, action={2}]", packetId, time, action); + } + } +} + diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/Decoder.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/Decoder.cs new file mode 100644 index 0000000..3f757ca --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/Decoder.cs @@ -0,0 +1,119 @@ +#region License +/* + * Decoder.cs + * + * The MIT License + * + * Copyright (c) 2014 Fabio Panettieri + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +//#define SOCKET_IO_DEBUG // Uncomment this for debug +using System; +using System.Collections; +using System.Text; +using UnityEngine; +using WebSocketSharp; + +namespace SocketIO +{ + public class Decoder + { + public Packet Decode(MessageEventArgs e) + { + try + { + #if SOCKET_IO_DEBUG + Debug.Log("[SocketIO] Decoding: " + e.Data); + #endif + + string data = e.Data; + Packet packet = new Packet(); + int offset = 0; + + // look up packet type + int enginePacketType = int.Parse(data.Substring(offset, 1)); + packet.enginePacketType = (EnginePacketType)enginePacketType; + + if (enginePacketType == (int)EnginePacketType.MESSAGE) { + int socketPacketType = int.Parse(data.Substring(++offset, 1)); + packet.socketPacketType = (SocketPacketType)socketPacketType; + } + + // connect message properly parsed + if (data.Length <= 2) { + #if SOCKET_IO_DEBUG + Debug.Log("[SocketIO] Decoded: " + packet); + #endif + return packet; + } + + // look up namespace (if any) + if ('/' == data [offset + 1]) { + StringBuilder builder = new StringBuilder(); + while (offset < data.Length - 1 && data[++offset] != ',') { + builder.Append(data [offset]); + } + packet.nsp = builder.ToString(); + } else { + packet.nsp = "/"; + } + + // look up id + char next = data [offset + 1]; + if (next != ' ' && char.IsNumber(next)) { + StringBuilder builder = new StringBuilder(); + while (offset < data.Length - 1) { + char c = data [++offset]; + if (char.IsNumber(c)) { + builder.Append(c); + } else { + --offset; + break; + } + } + packet.id = int.Parse(builder.ToString()); + } + + // look up json data + if (++offset < data.Length - 1) { + try { + #if SOCKET_IO_DEBUG + Debug.Log("[SocketIO] Parsing JSON: " + data.Substring(offset)); + #endif + packet.json = new JSONObject(data.Substring(offset)); + } catch (Exception ex) { + Debug.LogException(ex); + } + } + + #if SOCKET_IO_DEBUG + Debug.Log("[SocketIO] Decoded: " + packet); + #endif + + return packet; + + } catch(Exception ex) { + throw new SocketIOException("Packet decoding failed: " + e.Data ,ex); + } + } + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/Encoder.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/Encoder.cs new file mode 100644 index 0000000..dd8be8a --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/Encoder.cs @@ -0,0 +1,90 @@ +#region License +/* + * Encoder.cs + * + * The MIT License + * + * Copyright (c) 2014 Fabio Panettieri + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +//#define SOCKET_IO_DEBUG // Uncomment this for debug +using System; +using System.Collections; +using System.Text; +using UnityEngine; + +namespace SocketIO +{ + public class Encoder + { + public string Encode(Packet packet) + { + try + { + #if SOCKET_IO_DEBUG + Debug.Log("[SocketIO] Encoding: " + packet.json); + #endif + + StringBuilder builder = new StringBuilder(); + + // first is type + builder.Append((int)packet.enginePacketType); + if(!packet.enginePacketType.Equals(EnginePacketType.MESSAGE)){ + return builder.ToString(); + } + + builder.Append((int)packet.socketPacketType); + + // attachments if we have them + if (packet.socketPacketType == SocketPacketType.BINARY_EVENT || packet.socketPacketType == SocketPacketType.BINARY_ACK) { + builder.Append(packet.attachments); + builder.Append('-'); + } + + // if we have a namespace other than '/' + // we append it followed by a comma ',' + if (!string.IsNullOrEmpty(packet.nsp) && !packet.nsp.Equals("/")) { + builder.Append(packet.nsp); + builder.Append(','); + } + + // immediately followed by the id + if (packet.id > -1) { + builder.Append(packet.id); + } + + if (packet.json != null && !packet.json.ToString().Equals("null")) { + builder.Append(packet.json.ToString()); + } + + #if SOCKET_IO_DEBUG + Debug.Log("[SocketIO] Encoded: " + builder); + #endif + + return builder.ToString(); + + } catch(Exception ex) { + throw new SocketIOException("Packet encoding failed: " + packet ,ex); + } + } + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/EnginePacketType.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/EnginePacketType.cs new file mode 100644 index 0000000..1c847cc --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/EnginePacketType.cs @@ -0,0 +1,46 @@ +#region License +/* + * EnginePacketType.cs + * + * The MIT License + * + * Copyright (c) 2014 Fabio Panettieri + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion +using System; + +/** + * Message types + */ +namespace SocketIO +{ + public enum EnginePacketType + { + UNKNOWN = -1, + OPEN = 0, + CLOSE = 1, + PING = 2, + PONG = 3, + MESSAGE = 4, + UPGRADE = 5, + NOOP = 6 + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/Packet.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/Packet.cs new file mode 100644 index 0000000..7b73b95 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/Packet.cs @@ -0,0 +1,59 @@ +#region License +/* + * Packet.cs + * + * The MIT License + * + * Copyright (c) 2014 Fabio Panettieri + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +namespace SocketIO +{ + public class Packet + { + public EnginePacketType enginePacketType; + public SocketPacketType socketPacketType; + + public int attachments; + public string nsp; + public int id; + public JSONObject json; + + public Packet() : this(EnginePacketType.UNKNOWN, SocketPacketType.UNKNOWN, -1, "/", -1, null) { } + public Packet(EnginePacketType enginePacketType) : this(enginePacketType, SocketPacketType.UNKNOWN, -1, "/", -1, null) { } + + public Packet(EnginePacketType enginePacketType, SocketPacketType socketPacketType, int attachments, string nsp, int id, JSONObject json) + { + this.enginePacketType = enginePacketType; + this.socketPacketType = socketPacketType; + this.attachments = attachments; + this.nsp = nsp; + this.id = id; + this.json = json; + } + + public override string ToString() + { + return string.Format("[Packet: enginePacketType={0}, socketPacketType={1}, attachments={2}, nsp={3}, id={4}, json={5}]", enginePacketType, socketPacketType, attachments, nsp, id, json); + } + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/Parser.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/Parser.cs new file mode 100644 index 0000000..57df513 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/Parser.cs @@ -0,0 +1,55 @@ +#region License +/* + * Parser.cs + * + * The MIT License + * + * Copyright (c) 2014 Fabio Panettieri + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion +using UnityEngine; + +namespace SocketIO +{ + public class Parser + { + public SocketIOEvent Parse(JSONObject json) + { + if (json.Count < 1 || json.Count > 2) { + throw new SocketIOException("Invalid number of parameters received: " + json.Count); + } + + if (json[0].type != JSONObject.Type.STRING) { + throw new SocketIOException("Invalid parameter type. " + json[0].type + " received while expecting " + JSONObject.Type.STRING); + } + + if (json.Count == 1) { + return new SocketIOEvent(json[0].str); + } + + if (json[1].type != JSONObject.Type.OBJECT) { + throw new SocketIOException("Invalid argument type. " + json[1].type + " received while expecting " + JSONObject.Type.OBJECT); + } + + return new SocketIOEvent(json[0].str, json[1]); + } + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/SocketIOComponent.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/SocketIOComponent.cs new file mode 100644 index 0000000..aad25c3 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/SocketIOComponent.cs @@ -0,0 +1,427 @@ +#region License +/* + * SocketIO.cs + * + * The MIT License + * + * Copyright (c) 2014 Fabio Panettieri + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#endregion + +//#define SOCKET_IO_DEBUG // Uncomment this for debug +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; +using UnityEngine; +using WebSocketSharp; +using WebSocketSharp.Net; + +namespace SocketIO +{ + public class SocketIOComponent : MonoBehaviour + { + #region Public Properties + + public string url = "ws://10.0.1.67:3031//socket.io/?EIO=3&transport=websocket"; + public bool autoConnect = false; + public int reconnectDelay = 5; + public float ackExpirationTime = 30f; + public float pingInterval = 25f; + public float pingTimeout = 60f; + + public WebSocket socket { get { return ws; } } + public string sid { get; set; } + public bool IsConnected { get { return connected; } } + + #endregion + + #region Private Properties + + private volatile bool connected; + private volatile bool thPinging; + private volatile bool thPong; + private volatile bool wsConnected; + + private Thread socketThread; + private Thread pingThread; + private WebSocket ws; + + private Encoder encoder; + private Decoder decoder; + private Parser parser; + + private Dictionary>> handlers; + private List ackList; + + private int packetId; + + private object eventQueueLock; + private Queue eventQueue; + + private object ackQueueLock; + private Queue ackQueue; + + #endregion + + #if SOCKET_IO_DEBUG + public Action debugMethod; + #endif + + #region Unity interface + + public void Awake() + { + encoder = new Encoder(); + decoder = new Decoder(); + parser = new Parser(); + handlers = new Dictionary>>(); + ackList = new List(); + sid = null; + packetId = 0; + + ws = new WebSocket(url); + ws.OnOpen += OnOpen; + ws.OnMessage += OnMessage; + ws.OnError += OnError; + ws.OnClose += OnClose; + wsConnected = false; + + eventQueueLock = new object(); + eventQueue = new Queue(); + + ackQueueLock = new object(); + ackQueue = new Queue(); + + connected = false; + + #if SOCKET_IO_DEBUG + if(debugMethod == null) { debugMethod = Debug.Log; }; + #endif + } + + public void Start() + { + if (autoConnect) { Connect(); } + } + + public void Update() + { + lock(eventQueueLock){ + while(eventQueue.Count > 0){ + EmitEvent(eventQueue.Dequeue()); + } + } + + lock(ackQueueLock){ + while(ackQueue.Count > 0){ + InvokeAck(ackQueue.Dequeue()); + } + } + + if(wsConnected != ws.IsConnected){ + wsConnected = ws.IsConnected; + if(wsConnected){ + EmitEvent("connect"); + } else { + EmitEvent("disconnect"); + } + } + + // GC expired acks + if(ackList.Count == 0) { return; } + if(DateTime.Now.Subtract(ackList[0].time).TotalSeconds < ackExpirationTime){ return; } + ackList.RemoveAt(0); + } + + public void OnDestroy() + { + if (socketThread != null) { socketThread.Abort(); } + if (pingThread != null) { pingThread.Abort(); } + } + + public void OnApplicationQuit() + { + Close(); + } + + #endregion + + #region Public Interface + + public void SetHeader(string header, string value) { + ws.SetHeader(header, value); + } + + public void Connect() + { + connected = true; + + socketThread = new Thread(RunSocketThread); + socketThread.Start(ws); + + pingThread = new Thread(RunPingThread); + pingThread.Start(ws); + } + + public void Close() + { + EmitClose(); + connected = false; + } + + public void On(string ev, Action callback) + { + if (!handlers.ContainsKey(ev)) { + handlers[ev] = new List>(); + } + handlers[ev].Add(callback); + } + + public void Off(string ev, Action callback) + { + if (!handlers.ContainsKey(ev)) { + #if SOCKET_IO_DEBUG + debugMethod.Invoke("[SocketIO] No callbacks registered for event: " + ev); + #endif + return; + } + + List> l = handlers [ev]; + if (!l.Contains(callback)) { + #if SOCKET_IO_DEBUG + debugMethod.Invoke("[SocketIO] Couldn't remove callback action for event: " + ev); + #endif + return; + } + + l.Remove(callback); + if (l.Count == 0) { + handlers.Remove(ev); + } + } + + public void Emit(string ev) + { + EmitMessage(-1, string.Format("[\"{0}\"]", ev)); + } + + public void Emit(string ev, Action action) + { + EmitMessage(++packetId, string.Format("[\"{0}\"]", ev)); + ackList.Add(new Ack(packetId, action)); + } + + public void Emit(string ev, JSONObject data) + { + EmitMessage(-1, string.Format("[\"{0}\",{1}]", ev, data)); + } + + public void Emit(string ev, JSONObject data, Action action) + { + EmitMessage(++packetId, string.Format("[\"{0}\",{1}]", ev, data)); + ackList.Add(new Ack(packetId, action)); + } + + #endregion + + #region Private Methods + + private void RunSocketThread(object obj) + { + WebSocket webSocket = (WebSocket)obj; + while(connected){ + if(webSocket.IsConnected){ + Thread.Sleep(reconnectDelay); + } else { + webSocket.Connect(); + } + } + webSocket.Close(); + } + + private void RunPingThread(object obj) + { + WebSocket webSocket = (WebSocket)obj; + + int timeoutMilis = Mathf.FloorToInt(pingTimeout * 1000); + int intervalMilis = Mathf.FloorToInt(pingInterval * 1000); + + DateTime pingStart; + + while(connected) + { + if(!wsConnected){ + Thread.Sleep(reconnectDelay); + } else { + thPinging = true; + thPong = false; + + EmitPacket(new Packet(EnginePacketType.PING)); + pingStart = DateTime.Now; + + while(webSocket.IsConnected && thPinging && (DateTime.Now.Subtract(pingStart).TotalSeconds < timeoutMilis)){ + Thread.Sleep(200); + } + + if(!thPong){ + webSocket.Close(); + } + + Thread.Sleep(intervalMilis); + } + } + } + + private void EmitMessage(int id, string raw) + { + EmitPacket(new Packet(EnginePacketType.MESSAGE, SocketPacketType.EVENT, 0, "/", id, new JSONObject(raw))); + } + + private void EmitClose() + { + EmitPacket(new Packet(EnginePacketType.MESSAGE, SocketPacketType.DISCONNECT, 0, "/", -1, new JSONObject(""))); + EmitPacket(new Packet(EnginePacketType.CLOSE)); + } + + private void EmitPacket(Packet packet) + { + #if SOCKET_IO_DEBUG + debugMethod.Invoke("[SocketIO] " + packet); + #endif + + try { + ws.Send(encoder.Encode(packet)); + } catch(SocketIOException ex) { + #if SOCKET_IO_DEBUG + debugMethod.Invoke(ex.ToString()); + #endif + } + } + + private void OnOpen(object sender, EventArgs e) + { + EmitEvent("open"); + } + + private void OnMessage(object sender, MessageEventArgs e) + { + #if SOCKET_IO_DEBUG + debugMethod.Invoke("[SocketIO] Raw message: " + e.Data); + #endif + Packet packet = decoder.Decode(e); + + switch (packet.enginePacketType) { + case EnginePacketType.OPEN: HandleOpen(packet); break; + case EnginePacketType.CLOSE: EmitEvent("close"); break; + case EnginePacketType.PING: HandlePing(); break; + case EnginePacketType.PONG: HandlePong(); break; + case EnginePacketType.MESSAGE: HandleMessage(packet); break; + } + } + + private void HandleOpen(Packet packet) + { + #if SOCKET_IO_DEBUG + debugMethod.Invoke("[SocketIO] Socket.IO sid: " + packet.json["sid"].str); + #endif + sid = packet.json["sid"].str; + EmitEvent("open"); + } + + private void HandlePing() + { + EmitPacket(new Packet(EnginePacketType.PONG)); + } + + private void HandlePong() + { + thPong = true; + thPinging = false; + } + + private void HandleMessage(Packet packet) + { + if(packet.json == null) { return; } + + if(packet.socketPacketType == SocketPacketType.ACK){ + for(int i = 0; i < ackList.Count; i++){ + if(ackList[i].packetId != packet.id){ continue; } + lock(ackQueueLock){ ackQueue.Enqueue(packet); } + return; + } + + #if SOCKET_IO_DEBUG + debugMethod.Invoke("[SocketIO] Ack received for invalid Action: " + packet.id); + #endif + } + + if (packet.socketPacketType == SocketPacketType.EVENT) { + SocketIOEvent e = parser.Parse(packet.json); + lock(eventQueueLock){ eventQueue.Enqueue(e); } + } + } + + private void OnError(object sender, ErrorEventArgs e) + { + EmitEvent(new SocketIOEvent("error", JSONObject.CreateStringObject(e.Message))); + } + + private void OnClose(object sender, CloseEventArgs e) + { + EmitEvent("close"); + } + + private void EmitEvent(string type) + { + EmitEvent(new SocketIOEvent(type)); + } + + private void EmitEvent(SocketIOEvent ev) + { + if (!handlers.ContainsKey(ev.name)) { return; } + foreach (Action handler in this.handlers[ev.name]) { + try{ + handler(ev); + } catch(Exception ex){ + #if SOCKET_IO_DEBUG + debugMethod.Invoke(ex.ToString()); + #endif + } + } + } + + private void InvokeAck(Packet packet) + { + Ack ack; + for(int i = 0; i < ackList.Count; i++){ + if(ackList[i].packetId != packet.id){ continue; } + ack = ackList[i]; + ackList.RemoveAt(i); + ack.Invoke(packet.json); + return; + } + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/SocketIOEvent.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/SocketIOEvent.cs new file mode 100644 index 0000000..04a0532 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/SocketIOEvent.cs @@ -0,0 +1,50 @@ +#region License +/* + * SocketIOEvent.cs + * + * The MIT License + * + * Copyright (c) 2014 Fabio Panettieri + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +namespace SocketIO +{ + public class SocketIOEvent + { + public string name { get; set; } + + public JSONObject data { get; set; } + + public SocketIOEvent(string name) : this(name, null) { } + + public SocketIOEvent(string name, JSONObject data) + { + this.name = name; + this.data = data; + } + + public override string ToString() + { + return string.Format("[SocketIOEvent: name={0}, data={1}]", name, data); + } + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/SocketIOException.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/SocketIOException.cs new file mode 100644 index 0000000..beac3b7 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/SocketIOException.cs @@ -0,0 +1,38 @@ +#region License +/* + * SocketIOException.cs + * + * The MIT License + * + * Copyright (c) 2014 Fabio Panettieri + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion +using System; + +namespace SocketIO +{ + public class SocketIOException : Exception + { + public SocketIOException(){} + public SocketIOException(string message): base(message){} + public SocketIOException(string message, Exception innerException): base(message, innerException){} + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/SocketPacketType.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/SocketPacketType.cs new file mode 100644 index 0000000..dd307f3 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/SocketIO/SocketPacketType.cs @@ -0,0 +1,43 @@ +#region License +/* + * SocketPacketType.cs + * + * The MIT License + * + * Copyright (c) 2014 Fabio Panettieri + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +namespace SocketIO +{ + public enum SocketPacketType + { + UNKNOWN = -1, + CONNECT = 0, + DISCONNECT = 1, + EVENT = 2, + ACK = 3, + ERROR = 4, + BINARY_EVENT = 5, + BINARY_ACK = 6, + CONTROL = 7 + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/Test/TestSocketIO.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/Test/TestSocketIO.cs new file mode 100644 index 0000000..ae0130e --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Scripts/Test/TestSocketIO.cs @@ -0,0 +1,101 @@ +#region License +/* + * TestSocketIO.cs + * + * The MIT License + * + * Copyright (c) 2014 Fabio Panettieri + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System.Collections; +using UnityEngine; +using SocketIO; + +public class TestSocketIO : MonoBehaviour +{ + private SocketIOComponent socket; + + public void Start() + { + GameObject go = GameObject.Find("SocketIO"); + socket = go.GetComponent(); + + socket.On("open", TestOpen); + socket.On("boop", TestBoop); + socket.On("error", TestError); + socket.On("close", TestClose); + + StartCoroutine("BeepBoop"); + } + + private IEnumerator BeepBoop() + { + // wait 1 seconds and continue + yield return new WaitForSeconds(1); + + socket.Emit("beep"); + + // wait 3 seconds and continue + yield return new WaitForSeconds(3); + + socket.Emit("beep"); + + // wait 2 seconds and continue + yield return new WaitForSeconds(2); + + socket.Emit("beep"); + + // wait ONE FRAME and continue + yield return null; + + socket.Emit("beep"); + socket.Emit("beep"); + } + + public void TestOpen(SocketIOEvent e) + { + Debug.Log("[SocketIO] Open received: " + e.name + " " + e.data); + } + + public void TestBoop(SocketIOEvent e) + { + Debug.Log("[SocketIO] Boop received: " + e.name + " " + e.data); + + if (e.data == null) { return; } + + Debug.Log( + "#####################################################" + + "THIS: " + e.data.GetField("this").str + + "#####################################################" + ); + } + + public void TestError(SocketIOEvent e) + { + Debug.Log("[SocketIO] Error received: " + e.name + " " + e.data); + } + + public void TestClose(SocketIOEvent e) + { + Debug.Log("[SocketIO] Close received: " + e.name + " " + e.data); + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/Server/beep.js.zip b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Server/beep.js.zip new file mode 100644 index 0000000..1ec5fa2 Binary files /dev/null and b/Examples/MeshReceivingUnityClient/Assets/SocketIO/Server/beep.js.zip differ diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/AssemblyInfo.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/AssemblyInfo.cs new file mode 100644 index 0000000..c85deaa --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/AssemblyInfo.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("websocket-sharp")] +[assembly: AssemblyDescription("A C# implementation of the WebSocket protocol client and server")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("websocket-sharp.dll")] +[assembly: AssemblyCopyright("sta.blockhead")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.2.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/ByteOrder.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/ByteOrder.cs new file mode 100644 index 0000000..de8d42f --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/ByteOrder.cs @@ -0,0 +1,47 @@ +#region License +/* + * ByteOrder.cs + * + * The MIT License + * + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; + +namespace WebSocketSharp +{ + /// + /// Contains the values that indicate whether the byte order is a Little-endian or Big-endian. + /// + public enum ByteOrder : byte + { + /// + /// Indicates a Little-endian. + /// + Little, + /// + /// Indicates a Big-endian. + /// + Big + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/CloseEventArgs.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/CloseEventArgs.cs new file mode 100644 index 0000000..8bc58f1 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/CloseEventArgs.cs @@ -0,0 +1,113 @@ +#region License +/* + * CloseEventArgs.cs + * + * The MIT License + * + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Text; + +namespace WebSocketSharp +{ + /// + /// Contains the event data associated with a event. + /// + /// + /// A event occurs when the WebSocket connection has been closed. + /// If you would like to get the reason for the close, you should access the or + /// property. + /// + public class CloseEventArgs : EventArgs + { + #region Private Fields + + private bool _clean; + private ushort _code; + private string _reason; + + #endregion + + #region Internal Constructors + + internal CloseEventArgs (PayloadData payload) + { + var data = payload.ApplicationData; + var len = data.Length; + _code = len > 1 + ? data.SubArray (0, 2).ToUInt16 (ByteOrder.Big) + : (ushort) CloseStatusCode.NoStatusCode; + + _reason = len > 2 + ? Encoding.UTF8.GetString (data.SubArray (2, len - 2)) + : String.Empty; + } + + #endregion + + #region Public Properties + + /// + /// Gets the status code for the close. + /// + /// + /// A that represents the status code for the close if any. + /// + public ushort Code { + get { + return _code; + } + } + + /// + /// Gets the reason for the close. + /// + /// + /// A that represents the reason for the close if any. + /// + public string Reason { + get { + return _reason; + } + } + + /// + /// Gets a value indicating whether the WebSocket connection has been closed cleanly. + /// + /// + /// true if the WebSocket connection has been closed cleanly; otherwise, false. + /// + public bool WasClean { + get { + return _clean; + } + + internal set { + _clean = value; + } + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/CloseStatusCode.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/CloseStatusCode.cs new file mode 100644 index 0000000..065afb4 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/CloseStatusCode.cs @@ -0,0 +1,124 @@ +#region License +/* + * CloseStatusCode.cs + * + * The MIT License + * + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; + +namespace WebSocketSharp +{ + /// + /// Contains the values of the status code for the WebSocket connection close. + /// + /// + /// + /// The values of the status code are defined in + /// Section 7.4 + /// of RFC 6455. + /// + /// + /// "Reserved value" must not be set as a status code in a close control frame + /// by an endpoint. It's designated for use in applications expecting a status + /// code to indicate that the connection was closed due to the system grounds. + /// + /// + public enum CloseStatusCode : ushort + { + /// + /// Equivalent to close status 1000. + /// Indicates a normal close. + /// + Normal = 1000, + /// + /// Equivalent to close status 1001. + /// Indicates that an endpoint is going away. + /// + Away = 1001, + /// + /// Equivalent to close status 1002. + /// Indicates that an endpoint is terminating the connection due to a protocol error. + /// + ProtocolError = 1002, + /// + /// Equivalent to close status 1003. + /// Indicates that an endpoint is terminating the connection because it has received + /// an unacceptable type message. + /// + IncorrectData = 1003, + /// + /// Equivalent to close status 1004. + /// Still undefined. Reserved value. + /// + Undefined = 1004, + /// + /// Equivalent to close status 1005. + /// Indicates that no status code was actually present. Reserved value. + /// + NoStatusCode = 1005, + /// + /// Equivalent to close status 1006. + /// Indicates that the connection was closed abnormally. Reserved value. + /// + Abnormal = 1006, + /// + /// Equivalent to close status 1007. + /// Indicates that an endpoint is terminating the connection because it has received + /// a message that contains a data that isn't consistent with the type of the message. + /// + InconsistentData = 1007, + /// + /// Equivalent to close status 1008. + /// Indicates that an endpoint is terminating the connection because it has received + /// a message that violates its policy. + /// + PolicyViolation = 1008, + /// + /// Equivalent to close status 1009. + /// Indicates that an endpoint is terminating the connection because it has received + /// a message that is too big to process. + /// + TooBig = 1009, + /// + /// Equivalent to close status 1010. + /// Indicates that the client is terminating the connection because it has expected + /// the server to negotiate one or more extension, but the server didn't return them + /// in the handshake response. + /// + IgnoreExtension = 1010, + /// + /// Equivalent to close status 1011. + /// Indicates that the server is terminating the connection because it has encountered + /// an unexpected condition that prevented it from fulfilling the request. + /// + ServerError = 1011, + /// + /// Equivalent to close status 1015. + /// Indicates that the connection was closed due to a failure to perform a TLS handshake. + /// Reserved value. + /// + TlsHandshakeFailure = 1015 + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/CompressionMethod.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/CompressionMethod.cs new file mode 100644 index 0000000..f705468 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/CompressionMethod.cs @@ -0,0 +1,53 @@ +#region License +/* + * CompressionMethod.cs + * + * The MIT License + * + * Copyright (c) 2013-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; + +namespace WebSocketSharp +{ + /// + /// Contains the values of the compression method used to compress the message on the WebSocket + /// connection. + /// + /// + /// The values of the compression method are defined in + /// Compression + /// Extensions for WebSocket. + /// + public enum CompressionMethod : byte + { + /// + /// Indicates non compression. + /// + None, + /// + /// Indicates using DEFLATE. + /// + Deflate + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/ErrorEventArgs.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/ErrorEventArgs.cs new file mode 100644 index 0000000..85685f0 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/ErrorEventArgs.cs @@ -0,0 +1,74 @@ +#region License +/* + * ErrorEventArgs.cs + * + * The MIT License + * + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; + +namespace WebSocketSharp +{ + /// + /// Contains the event data associated with a event. + /// + /// + /// A event occurs when the gets an error. + /// If you would like to get the error message, you should access the + /// property. + /// + public class ErrorEventArgs : EventArgs + { + #region Private Fields + + private string _message; + + #endregion + + #region Internal Constructors + + internal ErrorEventArgs (string message) + { + _message = message; + } + + #endregion + + #region Public Properties + + /// + /// Gets the error message. + /// + /// + /// A that represents the error message. + /// + public string Message { + get { + return _message; + } + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Ext.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Ext.cs new file mode 100644 index 0000000..52d8a7d --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Ext.cs @@ -0,0 +1,1808 @@ +#region License +/* + * Ext.cs + * + * Some parts of this code are derived from Mono (http://www.mono-project.com): + * - GetStatusDescription is derived from System.Net.HttpListenerResponse.cs + * - IsPredefinedScheme is derived from System.Uri.cs + * - MaybeUri is derived from System.Uri.cs + * + * The MIT License + * + * Copyright (c) 2001 Garrett Rooney + * Copyright (c) 2003 Ian MacLean + * Copyright (c) 2003 Ben Maurer + * Copyright (c) 2003, 2005, 2009 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2009 Stephane Delcroix + * Copyright (c) 2010-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.IO; +using System.IO.Compression; +using System.Net.Sockets; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using WebSocketSharp.Net; +using WebSocketSharp.Net.WebSockets; +//using WebSocketSharp.Server; + +namespace WebSocketSharp +{ + /// + /// Provides a set of static methods for the websocket-sharp. + /// + public static class Ext + { + #region Private Const Fields + + private const string _tspecials = "()<>@,;:\\\"/[]?={} \t"; + + #endregion + + #region Private Methods + + private static byte [] compress (this byte [] value) + { + if (value.LongLength == 0) + //return new Byte [] { 0x00, 0x00, 0x00, 0xff, 0xff }; + return value; + + using (var input = new MemoryStream (value)) { + return input.compressToArray (); + } + } + + private static MemoryStream compress (this Stream stream) + { + var output = new MemoryStream (); + if (stream.Length == 0) + return output; + + stream.Position = 0; + using (var ds = new DeflateStream (output, CompressionMode.Compress, true)) { + stream.CopyTo (ds); + ds.Close (); // "BFINAL" set to 1. + output.Position = 0; + + return output; + } + } + + private static byte [] compressToArray (this Stream stream) + { + using (var comp = stream.compress ()) { + comp.Close (); + return comp.ToArray (); + } + } + + private static byte [] decompress (this byte [] value) + { + if (value.LongLength == 0) + return value; + + using (var input = new MemoryStream (value)) { + return input.decompressToArray (); + } + } + + private static MemoryStream decompress (this Stream stream) + { + var output = new MemoryStream (); + if (stream.Length == 0) + return output; + + stream.Position = 0; + using (var ds = new DeflateStream (stream, CompressionMode.Decompress, true)) { + ds.CopyTo (output, true); + return output; + } + } + + private static byte [] decompressToArray (this Stream stream) + { + using (var decomp = stream.decompress ()) { + decomp.Close (); + return decomp.ToArray (); + } + } + + private static byte [] readBytes (this Stream stream, byte [] buffer, int offset, int length) + { + var len = stream.Read (buffer, offset, length); + if (len < 1) + return buffer.SubArray (0, offset); + + var tmp = 0; + while (len < length) { + tmp = stream.Read (buffer, offset + len, length - len); + if (tmp < 1) + break; + + len += tmp; + } + + return len < length + ? buffer.SubArray (0, offset + len) + : buffer; + } + + private static bool readBytes ( + this Stream stream, byte [] buffer, int offset, int length, Stream dest) + { + var bytes = stream.readBytes (buffer, offset, length); + var len = bytes.Length; + dest.Write (bytes, 0, len); + + return len == offset + length; + } + + private static void times (this ulong n, Action act) + { + for (ulong i = 0; i < n; i++) + act (); + } + + #endregion + + #region Internal Methods + + internal static byte [] Append (this ushort code, string reason) + { + using (var buffer = new MemoryStream ()) { + var tmp = code.ToByteArrayInternally (ByteOrder.Big); + buffer.Write (tmp, 0, 2); + if (reason != null && reason.Length > 0) { + tmp = Encoding.UTF8.GetBytes (reason); + buffer.Write (tmp, 0, tmp.Length); + } + + buffer.Close (); + return buffer.ToArray (); + } + } + + internal static string CheckIfCanRead (this Stream stream) + { + return stream == null + ? "'stream' must not be null." + : !stream.CanRead + ? "'stream' cannot be read." + : null; + } + + internal static string CheckIfClosable (this WebSocketState state) + { + return state == WebSocketState.Closing + ? "While closing the WebSocket connection." + : state == WebSocketState.Closed + ? "The WebSocket connection has already been closed." + : null; + } + + internal static string CheckIfConnectable (this WebSocketState state) + { + return state == WebSocketState.Open || state == WebSocketState.Closing + ? "A WebSocket connection has already been established." + : null; + } + + internal static string CheckIfOpen (this WebSocketState state) + { + return state == WebSocketState.Connecting + ? "A WebSocket connection isn't established." + : state == WebSocketState.Closing + ? "While closing the WebSocket connection." + : state == WebSocketState.Closed + ? "The WebSocket connection has already been closed." + : null; + } + + /*internal static string CheckIfStart (this ServerState state) + { + return state == ServerState.Ready + ? "The server hasn't yet started." + : state == ServerState.ShuttingDown + ? "The server is shutting down." + : state == ServerState.Stop + ? "The server has already stopped." + : null; + } + + internal static string CheckIfStartable (this ServerState state) + { + return state == ServerState.Start + ? "The server has already started." + : state == ServerState.ShuttingDown + ? "The server is shutting down." + : null; + }*/ + + internal static string CheckIfValidCloseStatusCode (this ushort code) + { + return !code.IsCloseStatusCode () + ? "Invalid close status code." + : null; + } + + internal static string CheckIfValidControlData (this byte [] data, string paramName) + { + return data.Length > 125 + ? String.Format ("'{0}' length must be less.", paramName) + : null; + } + + internal static string CheckIfValidProtocols (this string [] protocols) + { + return protocols.Contains ( + protocol => protocol == null || protocol.Length == 0 || !protocol.IsToken ()) + ? "Contains an invalid value." + : protocols.ContainsTwice () + ? "Contains a value twice." + : null; + } + + internal static string CheckIfValidSendData (this byte [] data) + { + return data == null + ? "'data' must not be null." + : null; + } + + internal static string CheckIfValidSendData (this FileInfo file) + { + return file == null + ? "'file' must not be null." + : null; + } + + internal static string CheckIfValidSendData (this string data) + { + return data == null + ? "'data' must not be null." + : null; + } + + internal static string CheckIfValidServicePath (this string servicePath) + { + return servicePath == null || servicePath.Length == 0 + ? "'servicePath' must not be null or empty." + : servicePath [0] != '/' + ? "'servicePath' not absolute path." + : servicePath.IndexOfAny (new [] {'?', '#'}) != -1 + ? "'servicePath' must not contain either or both query and fragment components." + : null; + } + + internal static string CheckIfValidSessionID (this string id) + { + return id == null || id.Length == 0 + ? "'id' must not be null or empty." + : null; + } + + internal static void Close (this HttpListenerResponse response, HttpStatusCode code) + { + response.StatusCode = (int) code; + response.OutputStream.Close (); + } + + internal static void CloseWithAuthChallenge ( + this HttpListenerResponse response, string challenge) + { + response.Headers.SetInternally ("WWW-Authenticate", challenge, true); + response.Close (HttpStatusCode.Unauthorized); + } + + internal static byte [] Compress (this byte [] value, CompressionMethod method) + { + return method == CompressionMethod.Deflate + ? value.compress () + : value; + } + + internal static Stream Compress (this Stream stream, CompressionMethod method) + { + return method == CompressionMethod.Deflate + ? stream.compress () + : stream; + } + + internal static byte [] CompressToArray (this Stream stream, CompressionMethod method) + { + return method == CompressionMethod.Deflate + ? stream.compressToArray () + : stream.ToByteArray (); + } + + internal static bool Contains (this IEnumerable source, Func condition) + { + foreach (T elm in source) + if (condition (elm)) + return true; + + return false; + } + + internal static bool ContainsTwice (this string [] values) + { + var len = values.Length; + + Func contains = null; + contains = index => { + if (index < len - 1) { + for (var i = index + 1; i < len; i++) + if (values [i] == values [index]) + return true; + + return contains (++index); + } + + return false; + }; + + return contains (0); + } + + internal static T [] Copy (this T [] src, long length) + { + var dest = new T [length]; + Array.Copy (src, 0, dest, 0, length); + + return dest; + } + + internal static void CopyTo (this Stream src, Stream dest) + { + src.CopyTo (dest, false); + } + + internal static void CopyTo (this Stream src, Stream dest, bool setDefaultPosition) + { + var readLen = 0; + var bufferLen = 256; + var buffer = new byte [bufferLen]; + while ((readLen = src.Read (buffer, 0, bufferLen)) > 0) { + dest.Write (buffer, 0, readLen); + } + + if (setDefaultPosition) + dest.Position = 0; + } + + internal static byte [] Decompress (this byte [] value, CompressionMethod method) + { + return method == CompressionMethod.Deflate + ? value.decompress () + : value; + } + + internal static Stream Decompress (this Stream stream, CompressionMethod method) + { + return method == CompressionMethod.Deflate + ? stream.decompress () + : stream; + } + + internal static byte [] DecompressToArray (this Stream stream, CompressionMethod method) + { + return method == CompressionMethod.Deflate + ? stream.decompressToArray () + : stream.ToByteArray (); + } + + /// + /// Determines whether the specified equals the specified , + /// and invokes the specified Action<int> delegate at the same time. + /// + /// + /// true if equals ; + /// otherwise, false. + /// + /// + /// An to compare. + /// + /// + /// A to compare. + /// + /// + /// An Action<int> delegate that references the method(s) called at + /// the same time as comparing. An parameter to pass to + /// the method(s) is . + /// + /// + /// isn't between 0 and 255. + /// + internal static bool EqualsWith (this int value, char c, Action action) + { + if (value < 0 || value > 255) + throw new ArgumentOutOfRangeException ("value"); + + action (value); + return value == c - 0; + } + + /// + /// Gets the absolute path from the specified . + /// + /// + /// A that represents the absolute path if it's successfully found; + /// otherwise, . + /// + /// + /// A that represents a URI to get the absolute path from. + /// + internal static string GetAbsolutePath (this Uri uri) + { + if (uri.IsAbsoluteUri) + return uri.AbsolutePath; + + var original = uri.OriginalString; + if (original [0] != '/') + return null; + + var i = original.IndexOfAny (new [] {'?', '#'}); + return i > 0 + ? original.Substring (0, i) + : original; + } + + internal static string GetMessage (this CloseStatusCode code) + { + return code == CloseStatusCode.ProtocolError + ? "A WebSocket protocol error has occurred." + : code == CloseStatusCode.IncorrectData + ? "An incorrect data has been received." + : code == CloseStatusCode.Abnormal + ? "An exception has occurred." + : code == CloseStatusCode.InconsistentData + ? "An inconsistent data has been received." + : code == CloseStatusCode.PolicyViolation + ? "A policy violation has occurred." + : code == CloseStatusCode.TooBig + ? "A too big data has been received." + : code == CloseStatusCode.IgnoreExtension + ? "WebSocket client did not receive expected extension(s)." + : code == CloseStatusCode.ServerError + ? "WebSocket server got an internal error." + : code == CloseStatusCode.TlsHandshakeFailure + ? "An error has occurred while handshaking." + : String.Empty; + } + + internal static string GetNameInternal (this string nameAndValue, string separator) + { + var i = nameAndValue.IndexOf (separator); + return i > 0 + ? nameAndValue.Substring (0, i).Trim () + : null; + } + + internal static string GetValueInternal (this string nameAndValue, string separator) + { + var i = nameAndValue.IndexOf (separator); + return i >= 0 && i < nameAndValue.Length - 1 + ? nameAndValue.Substring (i + 1).Trim () + : null; + } + + internal static TcpListenerWebSocketContext GetWebSocketContext ( + this TcpClient client, string protocol, bool secure, X509Certificate cert, Logger logger) + { + return new TcpListenerWebSocketContext (client, protocol, secure, cert, logger); + } + + internal static bool IsCompressionExtension (this string value) + { + return value.StartsWith ("permessage-"); + } + + internal static bool IsPortNumber (this int value) + { + return value > 0 && value < 65536; + } + + internal static bool IsReserved (this ushort code) + { + return code == (ushort) CloseStatusCode.Undefined || + code == (ushort) CloseStatusCode.NoStatusCode || + code == (ushort) CloseStatusCode.Abnormal || + code == (ushort) CloseStatusCode.TlsHandshakeFailure; + } + + internal static bool IsReserved (this CloseStatusCode code) + { + return code == CloseStatusCode.Undefined || + code == CloseStatusCode.NoStatusCode || + code == CloseStatusCode.Abnormal || + code == CloseStatusCode.TlsHandshakeFailure; + } + + internal static bool IsText (this string value) + { + var len = value.Length; + for (var i = 0; i < len; i++) { + char c = value [i]; + if (c < 0x20 && !"\r\n\t".Contains (c)) + return false; + + if (c == 0x7f) + return false; + + if (c == '\n' && ++i < len) { + c = value [i]; + if (!" \t".Contains (c)) + return false; + } + } + + return true; + } + + internal static bool IsToken (this string value) + { + foreach (char c in value) + if (c < 0x20 || c >= 0x7f || _tspecials.Contains (c)) + return false; + + return true; + } + + internal static string Quote (this string value) + { + return value.IsToken () + ? value + : String.Format ("\"{0}\"", value.Replace ("\"", "\\\"")); + } + + internal static byte [] ReadBytes (this Stream stream, int length) + { + return stream.readBytes (new byte [length], 0, length); + } + + internal static byte [] ReadBytes (this Stream stream, long length, int bufferLength) + { + using (var result = new MemoryStream ()) { + var count = length / bufferLength; + var rem = (int) (length % bufferLength); + + var buffer = new byte [bufferLength]; + var end = false; + for (long i = 0; i < count; i++) { + if (!stream.readBytes (buffer, 0, bufferLength, result)) { + end = true; + break; + } + } + + if (!end && rem > 0) + stream.readBytes (new byte [rem], 0, rem, result); + + result.Close (); + return result.ToArray (); + } + } + + internal static void ReadBytesAsync ( + this Stream stream, int length, Action completed, Action error) + { + var buffer = new byte [length]; + stream.BeginRead ( + buffer, + 0, + length, + ar => { + try { + var len = stream.EndRead (ar); + var bytes = len < 1 + ? new byte [0] + : len < length + ? stream.readBytes (buffer, len, length - len) + : buffer; + + if (completed != null) + completed (bytes); + } + catch (Exception ex) { + if (error != null) + error (ex); + } + }, + null); + } + + internal static string RemovePrefix (this string value, params string [] prefixes) + { + var i = 0; + foreach (var prefix in prefixes) { + if (value.StartsWith (prefix)) { + i = prefix.Length; + break; + } + } + + return i > 0 + ? value.Substring (i) + : value; + } + + internal static T [] Reverse (this T [] array) + { + var len = array.Length; + T [] reverse = new T [len]; + + var end = len - 1; + for (var i = 0; i <= end; i++) + reverse [i] = array [end - i]; + + return reverse; + } + + internal static IEnumerable SplitHeaderValue ( + this string value, params char [] separator) + { + var len = value.Length; + var separators = new string (separator); + + var buffer = new StringBuilder (32); + var quoted = false; + var escaped = false; + + char c; + for (var i = 0; i < len; i++) { + c = value [i]; + if (c == '"') { + if (escaped) + escaped = !escaped; + else + quoted = !quoted; + } + else if (c == '\\') { + if (i < len - 1 && value [i + 1] == '"') + escaped = true; + } + else if (separators.Contains (c)) { + if (!quoted) { + yield return buffer.ToString (); + buffer.Length = 0; + + continue; + } + } + else { + } + + buffer.Append (c); + } + + if (buffer.Length > 0) + yield return buffer.ToString (); + } + + internal static byte [] ToByteArray (this Stream stream) + { + using (var output = new MemoryStream ()) { + stream.Position = 0; + stream.CopyTo (output); + output.Close (); + + return output.ToArray (); + } + } + + internal static byte [] ToByteArrayInternally (this ushort value, ByteOrder order) + { + var bytes = BitConverter.GetBytes (value); + if (!order.IsHostOrder ()) + Array.Reverse (bytes); + + return bytes; + } + + internal static byte [] ToByteArrayInternally (this ulong value, ByteOrder order) + { + var bytes = BitConverter.GetBytes (value); + if (!order.IsHostOrder ()) + Array.Reverse (bytes); + + return bytes; + } + + internal static CompressionMethod ToCompressionMethod (this string value) + { + foreach (CompressionMethod method in Enum.GetValues (typeof (CompressionMethod))) + if (method.ToExtensionString () == value) + return method; + + return CompressionMethod.None; + } + + internal static string ToExtensionString (this CompressionMethod method) + { + return method != CompressionMethod.None + ? String.Format ("permessage-{0}", method.ToString ().ToLower ()) + : String.Empty; + } + + internal static System.Net.IPAddress ToIPAddress (this string hostNameOrAddress) + { + try { + var addrs = System.Net.Dns.GetHostAddresses (hostNameOrAddress); + return addrs [0]; + } + catch { + return null; + } + } + + internal static List ToList (this IEnumerable source) + { + return new List (source); + } + + internal static ushort ToUInt16 (this byte [] src, ByteOrder srcOrder) + { + return BitConverter.ToUInt16 (src.ToHostOrder (srcOrder), 0); + } + + internal static ulong ToUInt64 (this byte [] src, ByteOrder srcOrder) + { + return BitConverter.ToUInt64 (src.ToHostOrder (srcOrder), 0); + } + + internal static string TrimEndSlash (this string value) + { + value = value.TrimEnd ('/'); + return value.Length > 0 + ? value + : "/"; + } + + /// + /// Tries to create a for WebSocket with the specified + /// . + /// + /// + /// true if a is successfully created; otherwise, false. + /// + /// + /// A that represents the WebSocket URL to try. + /// + /// + /// When this method returns, a that represents the WebSocket URL if + /// is valid; otherwise, . + /// + /// + /// When this method returns, a that represents the error message if + /// is invalid; otherwise, . + /// + internal static bool TryCreateWebSocketUri ( + this string uriString, out Uri result, out string message) + { + result = null; + if (uriString.Length == 0) { + message = "Must not be empty."; + return false; + } + + var uri = uriString.ToUri (); + if (!uri.IsAbsoluteUri) { + message = "Must be the absolute URI: " + uriString; + return false; + } + + var scheme = uri.Scheme; + if (scheme != "ws" && scheme != "wss") { + message = "The scheme part must be 'ws' or 'wss': " + scheme; + return false; + } + + var fragment = uri.Fragment; + if (fragment.Length > 0) { + message = "Must not contain the fragment component: " + uriString; + return false; + } + + var port = uri.Port; + if (port > 0) { + if (port > 65535) { + message = "The port part must be between 1 and 65535: " + port; + return false; + } + + if ((scheme == "ws" && port == 443) || (scheme == "wss" && port == 80)) { + message = String.Format ( + "Invalid pair of scheme and port: {0}, {1}", scheme, port); + return false; + } + } + else { + port = scheme == "ws" ? 80 : 443; + var url = String.Format ( + "{0}://{1}:{2}{3}", scheme, uri.Host, port, uri.PathAndQuery); + uri = url.ToUri (); + } + + result = uri; + message = String.Empty; + + return true; + } + + internal static string Unquote (this string value) + { + var start = value.IndexOf ('\"'); + var end = value.LastIndexOf ('\"'); + if (start < end) + value = value.Substring (start + 1, end - start - 1).Replace ("\\\"", "\""); + + return value.Trim (); + } + + internal static void WriteBytes (this Stream stream, byte [] value) + { + using (var src = new MemoryStream (value)) { + src.CopyTo (stream); + } + } + + #endregion + + #region Public Methods + + /// + /// Determines whether the specified contains any of characters + /// in the specified array of . + /// + /// + /// true if contains any of ; + /// otherwise, false. + /// + /// + /// A to test. + /// + /// + /// An array of that contains characters to find. + /// + public static bool Contains (this string value, params char [] chars) + { + return chars == null || chars.Length == 0 + ? true + : value == null || value.Length == 0 + ? false + : value.IndexOfAny (chars) != -1; + } + + /// + /// Determines whether the specified contains the entry + /// with the specified . + /// + /// + /// true if contains the entry + /// with ; otherwise, false. + /// + /// + /// A to test. + /// + /// + /// A that represents the key of the entry to find. + /// + public static bool Contains (this NameValueCollection collection, string name) + { + return collection == null || collection.Count == 0 + ? false + : collection [name] != null; + } + + /// + /// Determines whether the specified contains the entry + /// with the specified both and . + /// + /// + /// true if contains the entry + /// with both and ; + /// otherwise, false. + /// + /// + /// A to test. + /// + /// + /// A that represents the key of the entry to find. + /// + /// + /// A that represents the value of the entry to find. + /// + public static bool Contains (this NameValueCollection collection, string name, string value) + { + if (collection == null || collection.Count == 0) + return false; + + var values = collection [name]; + if (values == null) + return false; + + foreach (var v in values.Split (',')) + if (v.Trim ().Equals (value, StringComparison.OrdinalIgnoreCase)) + return true; + + return false; + } + + /// + /// Emits the specified delegate if it isn't . + /// + /// + /// A to emit. + /// + /// + /// An from which emits this . + /// + /// + /// A that contains no event data. + /// + public static void Emit (this EventHandler eventHandler, object sender, EventArgs e) + { + if (eventHandler != null) + eventHandler (sender, e); + } + + /// + /// Emits the specified EventHandler<TEventArgs> delegate + /// if it isn't . + /// + /// + /// An EventHandler<TEventArgs> to emit. + /// + /// + /// An from which emits this . + /// + /// + /// A TEventArgs that represents the event data. + /// + /// + /// The type of the event data generated by the event. + /// + public static void Emit ( + this EventHandler eventHandler, object sender, TEventArgs e) + where TEventArgs : EventArgs + { + if (eventHandler != null) + eventHandler (sender, e); + } + + /// + /// Gets the collection of the HTTP cookies from the specified HTTP . + /// + /// + /// A that receives a collection of the HTTP cookies. + /// + /// + /// A that contains a collection of the HTTP headers. + /// + /// + /// true if is a collection of the response headers; + /// otherwise, false. + /// + public static CookieCollection GetCookies (this NameValueCollection headers, bool response) + { + var name = response ? "Set-Cookie" : "Cookie"; + return headers == null || !headers.Contains (name) + ? new CookieCollection () + : CookieCollection.Parse (headers [name], response); + } + + /// + /// Gets the description of the specified HTTP status . + /// + /// + /// A that represents the description of the HTTP status code. + /// + /// + /// One of enum values, indicates the HTTP status codes. + /// + public static string GetDescription (this HttpStatusCode code) + { + return ((int) code).GetStatusDescription (); + } + + /// + /// Gets the name from the specified that contains a pair of name and + /// value separated by a separator string. + /// + /// + /// A that represents the name if any; otherwise, null. + /// + /// + /// A that contains a pair of name and value separated by a separator + /// string. + /// + /// + /// A that represents a separator string. + /// + public static string GetName (this string nameAndValue, string separator) + { + return (nameAndValue != null && nameAndValue.Length > 0) && + (separator != null && separator.Length > 0) + ? nameAndValue.GetNameInternal (separator) + : null; + } + + /// + /// Gets the name and value from the specified that contains a pair of + /// name and value separated by a separator string. + /// + /// + /// A KeyValuePair<string, string> that represents the name and value if any. + /// + /// + /// A that contains a pair of name and value separated by a separator + /// string. + /// + /// + /// A that represents a separator string. + /// + public static KeyValuePair GetNameAndValue ( + this string nameAndValue, string separator) + { + var name = nameAndValue.GetName (separator); + var value = nameAndValue.GetValue (separator); + return name != null + ? new KeyValuePair (name, value) + : new KeyValuePair (null, null); + } + + /// + /// Gets the description of the specified HTTP status . + /// + /// + /// A that represents the description of the HTTP status code. + /// + /// + /// An that represents the HTTP status code. + /// + public static string GetStatusDescription (this int code) + { + switch (code) { + case 100: return "Continue"; + case 101: return "Switching Protocols"; + case 102: return "Processing"; + case 200: return "OK"; + case 201: return "Created"; + case 202: return "Accepted"; + case 203: return "Non-Authoritative Information"; + case 204: return "No Content"; + case 205: return "Reset Content"; + case 206: return "Partial Content"; + case 207: return "Multi-Status"; + case 300: return "Multiple Choices"; + case 301: return "Moved Permanently"; + case 302: return "Found"; + case 303: return "See Other"; + case 304: return "Not Modified"; + case 305: return "Use Proxy"; + case 307: return "Temporary Redirect"; + case 400: return "Bad Request"; + case 401: return "Unauthorized"; + case 402: return "Payment Required"; + case 403: return "Forbidden"; + case 404: return "Not Found"; + case 405: return "Method Not Allowed"; + case 406: return "Not Acceptable"; + case 407: return "Proxy Authentication Required"; + case 408: return "Request Timeout"; + case 409: return "Conflict"; + case 410: return "Gone"; + case 411: return "Length Required"; + case 412: return "Precondition Failed"; + case 413: return "Request Entity Too Large"; + case 414: return "Request-Uri Too Long"; + case 415: return "Unsupported Media Type"; + case 416: return "Requested Range Not Satisfiable"; + case 417: return "Expectation Failed"; + case 422: return "Unprocessable Entity"; + case 423: return "Locked"; + case 424: return "Failed Dependency"; + case 500: return "Internal Server Error"; + case 501: return "Not Implemented"; + case 502: return "Bad Gateway"; + case 503: return "Service Unavailable"; + case 504: return "Gateway Timeout"; + case 505: return "Http Version Not Supported"; + case 507: return "Insufficient Storage"; + } + + return String.Empty; + } + + /// + /// Gets the value from the specified that contains a pair of name and + /// value separated by a separator string. + /// + /// + /// A that represents the value if any; otherwise, null. + /// + /// + /// A that contains a pair of name and value separated by a separator + /// string. + /// + /// + /// A that represents a separator string. + /// + public static string GetValue (this string nameAndValue, string separator) + { + return (nameAndValue != null && nameAndValue.Length > 0) && + (separator != null && separator.Length > 0) + ? nameAndValue.GetValueInternal (separator) + : null; + } + + /// + /// Determines whether the specified is in the allowable range of + /// the WebSocket close status code. + /// + /// + /// Not allowable ranges are the followings. + /// + /// + /// + /// Numbers in the range 0-999 are not used. + /// + /// + /// + /// + /// Numbers greater than 4999 are out of the reserved close status code ranges. + /// + /// + /// + /// + /// + /// true if is in the allowable range of the WebSocket + /// close status code; otherwise, false. + /// + /// + /// A to test. + /// + public static bool IsCloseStatusCode (this ushort value) + { + return value > 999 && value < 5000; + } + + /// + /// Determines whether the specified is enclosed in the specified + /// . + /// + /// + /// true if is enclosed in ; + /// otherwise, false. + /// + /// + /// A to test. + /// + /// + /// A that represents the character to find. + /// + public static bool IsEnclosedIn (this string value, char c) + { + return value != null && + value.Length > 1 && + value [0] == c && + value [value.Length - 1] == c; + } + + /// + /// Determines whether the specified is host + /// (this computer architecture) byte order. + /// + /// + /// true if is host byte order; + /// otherwise, false. + /// + /// + /// One of the enum values, to test. + /// + public static bool IsHostOrder (this ByteOrder order) + { + // true : !(true ^ true) or !(false ^ false) + // false: !(true ^ false) or !(false ^ true) + return !(BitConverter.IsLittleEndian ^ (order == ByteOrder.Little)); + } + + /// + /// Determines whether the specified represents + /// the local IP address. + /// + /// + /// true if represents the local IP address; + /// otherwise, false. + /// + /// + /// A to test. + /// + /// + /// is . + /// + public static bool IsLocal (this System.Net.IPAddress address) + { + if (address == null) + throw new ArgumentNullException ("address"); + + if (address.Equals (System.Net.IPAddress.Any) || System.Net.IPAddress.IsLoopback (address)) + return true; + + var host = System.Net.Dns.GetHostName (); + var addrs = System.Net.Dns.GetHostAddresses (host); + foreach (var addr in addrs) + if (address.Equals (addr)) + return true; + + return false; + } + + /// + /// Determines whether the specified is or empty. + /// + /// + /// true if is or empty; + /// otherwise, false. + /// + /// + /// A to test. + /// + public static bool IsNullOrEmpty (this string value) + { + return value == null || value.Length == 0; + } + + /// + /// Determines whether the specified is a predefined scheme. + /// + /// + /// true if is a predefined scheme; otherwise, false. + /// + /// + /// A to test. + /// + public static bool IsPredefinedScheme (this string value) + { + if (value == null || value.Length < 2) + return false; + + var c = value [0]; + if (c == 'h') + return value == "http" || value == "https"; + + if (c == 'w') + return value == "ws" || value == "wss"; + + if (c == 'f') + return value == "file" || value == "ftp"; + + if (c == 'n') { + c = value [1]; + return c == 'e' + ? value == "news" || value == "net.pipe" || value == "net.tcp" + : value == "nntp"; + } + + return (c == 'g' && value == "gopher") || (c == 'm' && value == "mailto"); + } + + /// + /// Determines whether the specified is an HTTP Upgrade + /// request to switch to the specified . + /// + /// + /// true if is an HTTP Upgrade request to switch to + /// ; otherwise, false. + /// + /// + /// A that represents the HTTP request. + /// + /// + /// A that represents the protocol name. + /// + /// + /// + /// is . + /// + /// + /// -or- + /// + /// + /// is . + /// + /// + /// + /// is empty. + /// + public static bool IsUpgradeTo (this HttpListenerRequest request, string protocol) + { + if (request == null) + throw new ArgumentNullException ("request"); + + if (protocol == null) + throw new ArgumentNullException ("protocol"); + + if (protocol.Length == 0) + throw new ArgumentException ("Must not be empty.", "protocol"); + + return request.Headers.Contains ("Upgrade", protocol) && + request.Headers.Contains ("Connection", "Upgrade"); + } + + /// + /// Determines whether the specified is a URI string. + /// + /// + /// true if may be a URI string; otherwise, false. + /// + /// + /// A to test. + /// + public static bool MaybeUri (this string value) + { + if (value == null || value.Length == 0) + return false; + + var i = value.IndexOf (':'); + if (i == -1) + return false; + + if (i >= 10) + return false; + + return value.Substring (0, i).IsPredefinedScheme (); + } + + /// + /// Retrieves a sub-array from the specified . + /// A sub-array starts at the specified element position. + /// + /// + /// An array of T that receives a sub-array, or an empty array of T if any problems + /// with the parameters. + /// + /// + /// An array of T that contains the data to retrieve a sub-array. + /// + /// + /// An that contains the zero-based starting position of a sub-array + /// in . + /// + /// + /// An that contains the number of elements to retrieve a sub-array. + /// + /// + /// The type of elements in the . + /// + public static T [] SubArray (this T [] array, int startIndex, int length) + { + if (array == null || array.Length == 0) + return new T [0]; + + if (startIndex < 0 || length <= 0) + return new T [0]; + + if (startIndex + length > array.Length) + return new T [0]; + + if (startIndex == 0 && array.Length == length) + return array; + + T [] subArray = new T [length]; + Array.Copy (array, startIndex, subArray, 0, length); + + return subArray; + } + + /// + /// Executes the specified delegate times. + /// + /// + /// An is the number of times to execute. + /// + /// + /// An delegate that references the method(s) to execute. + /// + public static void Times (this int n, Action act) + { + if (n > 0 && act != null) + ((ulong) n).times (act); + } + + /// + /// Executes the specified delegate times. + /// + /// + /// A is the number of times to execute. + /// + /// + /// An delegate that references the method(s) to execute. + /// + public static void Times (this long n, Action act) + { + if (n > 0 && act != null) + ((ulong) n).times (act); + } + + /// + /// Executes the specified delegate times. + /// + /// + /// A is the number of times to execute. + /// + /// + /// An delegate that references the method(s) to execute. + /// + public static void Times (this uint n, Action act) + { + if (n > 0 && act != null) + ((ulong) n).times (act); + } + + /// + /// Executes the specified delegate times. + /// + /// + /// A is the number of times to execute. + /// + /// + /// An delegate that references the method(s) to execute. + /// + public static void Times (this ulong n, Action act) + { + if (n > 0 && act != null) + n.times (act); + } + + /// + /// Executes the specified Action<int> delegate times. + /// + /// + /// An is the number of times to execute. + /// + /// + /// An Action<int> delegate that references the method(s) to execute. + /// An parameter to pass to the method(s) is the zero-based count + /// of iteration. + /// + public static void Times (this int n, Action act) + { + if (n > 0 && act != null) + for (int i = 0; i < n; i++) + act (i); + } + + /// + /// Executes the specified Action<long> delegate times. + /// + /// + /// A is the number of times to execute. + /// + /// + /// An Action<long> delegate that references the method(s) to execute. + /// A parameter to pass to the method(s) is the zero-based count + /// of iteration. + /// + public static void Times (this long n, Action act) + { + if (n > 0 && act != null) + for (long i = 0; i < n; i++) + act (i); + } + + /// + /// Executes the specified Action<uint> delegate times. + /// + /// + /// A is the number of times to execute. + /// + /// + /// An Action<uint> delegate that references the method(s) to execute. + /// A parameter to pass to the method(s) is the zero-based count + /// of iteration. + /// + public static void Times (this uint n, Action act) + { + if (n > 0 && act != null) + for (uint i = 0; i < n; i++) + act (i); + } + + /// + /// Executes the specified Action<ulong> delegate times. + /// + /// + /// A is the number of times to execute. + /// + /// + /// An Action<ulong> delegate that references the method(s) to execute. + /// A parameter to pass to this method(s) is the zero-based count + /// of iteration. + /// + public static void Times (this ulong n, Action act) + { + if (n > 0 && act != null) + for (ulong i = 0; i < n; i++) + act (i); + } + + /// + /// Converts the specified array of to the specified type data. + /// + /// + /// A T converted from , or a default value of T if + /// is an empty array of or if the + /// type of T isn't , , , + /// , , , , + /// , , or . + /// + /// + /// An array of to convert. + /// + /// + /// One of the enum values, indicates the byte order of + /// . + /// + /// + /// The type of the return. The T must be a value type. + /// + /// + /// is . + /// + public static T To (this byte [] src, ByteOrder srcOrder) + where T : struct + { + if (src == null) + throw new ArgumentNullException ("src"); + + if (src.Length == 0) + return default (T); + + var type = typeof (T); + var buffer = src.ToHostOrder (srcOrder); + + return type == typeof (Boolean) + ? (T)(object) BitConverter.ToBoolean (buffer, 0) + : type == typeof (Char) + ? (T)(object) BitConverter.ToChar (buffer, 0) + : type == typeof (Double) + ? (T)(object) BitConverter.ToDouble (buffer, 0) + : type == typeof (Int16) + ? (T)(object) BitConverter.ToInt16 (buffer, 0) + : type == typeof (Int32) + ? (T)(object) BitConverter.ToInt32 (buffer, 0) + : type == typeof (Int64) + ? (T)(object) BitConverter.ToInt64 (buffer, 0) + : type == typeof (Single) + ? (T)(object) BitConverter.ToSingle (buffer, 0) + : type == typeof (UInt16) + ? (T)(object) BitConverter.ToUInt16 (buffer, 0) + : type == typeof (UInt32) + ? (T)(object) BitConverter.ToUInt32 (buffer, 0) + : type == typeof (UInt64) + ? (T)(object) BitConverter.ToUInt64 (buffer, 0) + : default (T); + } + + /// + /// Converts the specified to an array of . + /// + /// + /// An array of converted from . + /// + /// + /// A T to convert. + /// + /// + /// One of the enum values, indicates the byte order of the return. + /// + /// + /// The type of . The T must be a value type. + /// + public static byte [] ToByteArray (this T value, ByteOrder order) + where T : struct + { + var type = typeof (T); + var bytes = type == typeof (Boolean) + ? BitConverter.GetBytes ((Boolean)(object) value) + : type == typeof (Byte) + ? new byte [] { (Byte)(object) value } + : type == typeof (Char) + ? BitConverter.GetBytes ((Char)(object) value) + : type == typeof (Double) + ? BitConverter.GetBytes ((Double)(object) value) + : type == typeof (Int16) + ? BitConverter.GetBytes ((Int16)(object) value) + : type == typeof (Int32) + ? BitConverter.GetBytes ((Int32)(object) value) + : type == typeof (Int64) + ? BitConverter.GetBytes ((Int64)(object) value) + : type == typeof (Single) + ? BitConverter.GetBytes ((Single)(object) value) + : type == typeof (UInt16) + ? BitConverter.GetBytes ((UInt16)(object) value) + : type == typeof (UInt32) + ? BitConverter.GetBytes ((UInt32)(object) value) + : type == typeof (UInt64) + ? BitConverter.GetBytes ((UInt64)(object) value) + : new byte [0]; + + if (bytes.Length > 1 && !order.IsHostOrder ()) + Array.Reverse (bytes); + + return bytes; + } + + /// + /// Converts the order of the specified array of to the host byte order. + /// + /// + /// An array of converted from . + /// + /// + /// An array of to convert. + /// + /// + /// One of the enum values, indicates the byte order of + /// . + /// + /// + /// is . + /// + public static byte [] ToHostOrder (this byte [] src, ByteOrder srcOrder) + { + if (src == null) + throw new ArgumentNullException ("src"); + + return src.Length > 1 && !srcOrder.IsHostOrder () + ? src.Reverse () + : src; + } + + /// + /// Converts the specified to a + /// that concatenates the each element of across the + /// specified . + /// + /// + /// A converted from , + /// or if is empty. + /// + /// + /// An array of T to convert. + /// + /// + /// A that represents the separator string. + /// + /// + /// The type of elements in . + /// + /// + /// is . + /// + public static string ToString (this T [] array, string separator) + { + if (array == null) + throw new ArgumentNullException ("array"); + + var len = array.Length; + if (len == 0) + return String.Empty; + + if (separator == null) + separator = String.Empty; + + var buffer = new StringBuilder (64); + (len - 1).Times (i => buffer.AppendFormat ("{0}{1}", array [i].ToString (), separator)); + + buffer.Append (array [len - 1].ToString ()); + return buffer.ToString (); + } + + /// + /// Converts the specified to a . + /// + /// + /// A converted from , or + /// if isn't successfully converted. + /// + /// + /// A to convert. + /// + public static Uri ToUri (this string uriString) + { + Uri res; + return Uri.TryCreate ( + uriString, uriString.MaybeUri () ? UriKind.Absolute : UriKind.Relative, out res) + ? res + : null; + } + + /// + /// URL-decodes the specified . + /// + /// + /// A that receives the decoded string, or the + /// if it's or empty. + /// + /// + /// A to decode. + /// + public static string UrlDecode (this string value) + { + return value == null || value.Length == 0 + ? value + : HttpUtility.UrlDecode (value); + } + + /// + /// URL-encodes the specified . + /// + /// + /// A that receives the encoded string, or + /// if it's or empty. + /// + /// + /// A to encode. + /// + public static string UrlEncode (this string value) + { + return value == null || value.Length == 0 + ? value + : HttpUtility.UrlEncode (value); + } + + /// + /// Writes the specified data with the specified + /// . + /// + /// + /// A that represents the HTTP response + /// used to write the content data. + /// + /// + /// An array of that represents the content data to write. + /// + /// + /// is . + /// + public static void WriteContent (this HttpListenerResponse response, byte [] content) + { + if (response == null) + throw new ArgumentNullException ("response"); + + if (content == null || content.Length == 0) + return; + + var output = response.OutputStream; + response.ContentLength64 = content.Length; + output.Write (content, 0, content.Length); + output.Close (); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Fin.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Fin.cs new file mode 100644 index 0000000..12aa9fa --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Fin.cs @@ -0,0 +1,38 @@ +#region License +/* + * Fin.cs + * + * The MIT License + * + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; + +namespace WebSocketSharp +{ + internal enum Fin : byte + { + More = 0x0, + Final = 0x1 + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/HandshakeBase.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/HandshakeBase.cs new file mode 100644 index 0000000..68de881 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/HandshakeBase.cs @@ -0,0 +1,121 @@ +#region License +/* + * HandshakeBase.cs + * + * The MIT License + * + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Collections.Specialized; +using System.Text; +using WebSocketSharp.Net; + +namespace WebSocketSharp +{ + internal abstract class HandshakeBase + { + #region Private Fields + + private NameValueCollection _headers; + private Version _version; + + #endregion + + #region Internal Fields + + internal byte[] EntityBodyData; + + #endregion + + #region Protected Const Fields + + protected const string CrLf = "\r\n"; + + #endregion + + #region Protected Constructors + + protected HandshakeBase (Version version, NameValueCollection headers) + { + _version = version; + _headers = headers; + } + + #endregion + + #region Public Properties + + public string EntityBody { + get { + return EntityBodyData != null && EntityBodyData.LongLength > 0 + ? getEncoding (_headers["Content-Type"]).GetString (EntityBodyData) + : String.Empty; + } + } + + public NameValueCollection Headers { + get { + return _headers; + } + } + + public Version ProtocolVersion { + get { + return _version; + } + } + + #endregion + + #region Private Methods + + private static Encoding getEncoding (string contentType) + { + if (contentType == null || contentType.Length == 0) + return Encoding.UTF8; + + var i = contentType.IndexOf ("charset=", StringComparison.Ordinal); + if (i == -1) + return Encoding.UTF8; + + var charset = contentType.Substring (i + 8); + i = charset.IndexOf (';'); + if (i != -1) + charset = charset.Substring (0, i); + + return Encoding.GetEncoding (charset); + } + + #endregion + + #region Public Methods + + public byte[] ToByteArray () + { + return Encoding.UTF8.GetBytes (ToString ()); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/HandshakeRequest.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/HandshakeRequest.cs new file mode 100644 index 0000000..c7c8433 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/HandshakeRequest.cs @@ -0,0 +1,179 @@ +#region License +/* + * HandshakeRequest.cs + * + * The MIT License + * + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Collections.Specialized; +using System.Text; +using WebSocketSharp.Net; + +namespace WebSocketSharp +{ + internal class HandshakeRequest : HandshakeBase + { + #region Private Fields + + private string _method; + private string _uri; + private bool _websocketRequest; + private bool _websocketRequestWasSet; + + #endregion + + #region Private Constructors + + private HandshakeRequest (Version version, NameValueCollection headers) + : base (version, headers) + { + } + + #endregion + + #region Internal Constructors + + internal HandshakeRequest (string pathAndQuery) + : base (HttpVersion.Version11, new NameValueCollection ()) + { + _uri = pathAndQuery; + _method = "GET"; + + var headers = Headers; + headers["User-Agent"] = "websocket-sharp/1.0"; + headers["Upgrade"] = "websocket"; + headers["Connection"] = "Upgrade"; + } + + #endregion + + #region Public Properties + + public AuthenticationResponse AuthResponse { + get { + var auth = Headers["Authorization"]; + return auth != null && auth.Length > 0 + ? AuthenticationResponse.Parse (auth) + : null; + } + } + + public CookieCollection Cookies { + get { + return Headers.GetCookies (false); + } + } + + public string HttpMethod { + get { + return _method; + } + } + + public bool IsWebSocketRequest { + get { + if (!_websocketRequestWasSet) { + var headers = Headers; + _websocketRequest = _method == "GET" && + ProtocolVersion > HttpVersion.Version10 && + headers.Contains ("Upgrade", "websocket") && + headers.Contains ("Connection", "Upgrade"); + + _websocketRequestWasSet = true; + } + + return _websocketRequest; + } + } + + public string RequestUri { + get { + return _uri; + } + } + + #endregion + + #region Internal Methods + + internal static HandshakeRequest Parse (string[] headerParts) + { + var requestLine = headerParts[0].Split (new[] { ' ' }, 3); + if (requestLine.Length != 3) + throw new ArgumentException ("Invalid request line: " + headerParts[0]); + + var headers = new WebHeaderCollection (); + for (int i = 1; i < headerParts.Length; i++) + headers.SetInternally (headerParts[i], false); + + var req = new HandshakeRequest (new Version (requestLine[2].Substring (5)), headers); + req._method = requestLine[0]; + req._uri = requestLine[1]; + + return req; + } + + #endregion + + #region Public Methods + + public void SetCookies (CookieCollection cookies) + { + if (cookies == null || cookies.Count == 0) + return; + + var buff = new StringBuilder (64); + foreach (var cookie in cookies.Sorted) + if (!cookie.Expired) + buff.AppendFormat ("{0}; ", cookie.ToString ()); + + var len = buff.Length; + if (len > 2) { + buff.Length = len - 2; + Headers["Cookie"] = buff.ToString (); + } + } + + public override string ToString () + { + var output = new StringBuilder (64); + output.AppendFormat ("{0} {1} HTTP/{2}{3}", _method, _uri, ProtocolVersion, CrLf); + + var headers = Headers; + foreach (var key in headers.AllKeys) + output.AppendFormat ("{0}: {1}{2}", key, headers[key], CrLf); + + output.Append (CrLf); + + var entity = EntityBody; + if (entity.Length > 0) + output.Append (entity); + + return output.ToString (); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/HandshakeResponse.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/HandshakeResponse.cs new file mode 100644 index 0000000..f70768e --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/HandshakeResponse.cs @@ -0,0 +1,180 @@ +#region License +/* + * HandshakeResponse.cs + * + * The MIT License + * + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Collections.Specialized; +using System.Text; +using WebSocketSharp.Net; + +namespace WebSocketSharp +{ + internal class HandshakeResponse : HandshakeBase + { + #region Private Fields + + private string _code; + private string _reason; + + #endregion + + #region Private Constructors + + private HandshakeResponse (Version version, NameValueCollection headers) + : base (version, headers) + { + } + + #endregion + + #region Internal Constructors + + internal HandshakeResponse (HttpStatusCode code) + : base (HttpVersion.Version11, new NameValueCollection ()) + { + _code = ((int) code).ToString (); + _reason = code.GetDescription (); + + var headers = Headers; + headers["Server"] = "websocket-sharp/1.0"; + if (code == HttpStatusCode.SwitchingProtocols) { + headers["Upgrade"] = "websocket"; + headers["Connection"] = "Upgrade"; + } + } + + #endregion + + #region Public Properties + + public AuthenticationChallenge AuthChallenge { + get { + var auth = Headers["WWW-Authenticate"]; + return auth != null && auth.Length > 0 + ? AuthenticationChallenge.Parse (auth) + : null; + } + } + + public CookieCollection Cookies { + get { + return Headers.GetCookies (true); + } + } + + public bool IsUnauthorized { + get { + return _code == "401"; + } + } + + public bool IsWebSocketResponse { + get { + var headers = Headers; + return ProtocolVersion > HttpVersion.Version10 && + _code == "101" && + headers.Contains ("Upgrade", "websocket") && + headers.Contains ("Connection", "Upgrade"); + } + } + + public string Reason { + get { + return _reason; + } + } + + public string StatusCode { + get { + return _code; + } + } + + #endregion + + #region Internal Methods + + internal static HandshakeResponse CreateCloseResponse (HttpStatusCode code) + { + var res = new HandshakeResponse (code); + res.Headers["Connection"] = "close"; + + return res; + } + + internal static HandshakeResponse Parse (string[] headerParts) + { + var statusLine = headerParts[0].Split (new[] { ' ' }, 3); + if (statusLine.Length != 3) + throw new ArgumentException ("Invalid status line: " + headerParts[0]); + + var headers = new WebHeaderCollection (); + for (int i = 1; i < headerParts.Length; i++) + headers.SetInternally (headerParts[i], true); + + var res = new HandshakeResponse (new Version (statusLine[0].Substring (5)), headers); + res._code = statusLine[1]; + res._reason = statusLine[2]; + + return res; + } + + #endregion + + #region Public Methods + + public void SetCookies (CookieCollection cookies) + { + if (cookies == null || cookies.Count == 0) + return; + + var headers = Headers; + foreach (var cookie in cookies.Sorted) + headers.Add ("Set-Cookie", cookie.ToResponseString ()); + } + + public override string ToString () + { + var output = new StringBuilder (64); + output.AppendFormat ("HTTP/{0} {1} {2}{3}", ProtocolVersion, _code, _reason, CrLf); + + var headers = Headers; + foreach (var key in headers.AllKeys) + output.AppendFormat ("{0}: {1}{2}", key, headers[key], CrLf); + + output.Append (CrLf); + + var entity = EntityBody; + if (entity.Length > 0) + output.Append (entity); + + return output.ToString (); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/LogData.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/LogData.cs new file mode 100644 index 0000000..fe0e7fa --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/LogData.cs @@ -0,0 +1,152 @@ +#region License +/* + * LogData.cs + * + * The MIT License + * + * Copyright (c) 2013-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Diagnostics; +using System.Text; + +namespace WebSocketSharp +{ + /// + /// Represents a log data used by the class. + /// + public class LogData + { + #region Private Fields + + private StackFrame _caller; + private DateTime _date; + private LogLevel _level; + private string _message; + + #endregion + + #region Internal Constructors + + internal LogData (LogLevel level, StackFrame caller, string message) + { + _level = level; + _caller = caller; + _message = message ?? String.Empty; + _date = DateTime.Now; + } + + #endregion + + #region Public Properties + + /// + /// Gets the information of the logging method caller. + /// + /// + /// A that provides the information of the logging method caller. + /// + public StackFrame Caller { + get { + return _caller; + } + } + + /// + /// Gets the date and time when the log data was created. + /// + /// + /// A that represents the date and time when the log data was created. + /// + public DateTime Date { + get { + return _date; + } + } + + /// + /// Gets the logging level of the log data. + /// + /// + /// One of the enum values, indicates the logging level of the log data. + /// + public LogLevel Level { + get { + return _level; + } + } + + /// + /// Gets the message of the log data. + /// + /// + /// A that represents the message of the log data. + /// + public string Message { + get { + return _message; + } + } + + #endregion + + #region Public Methods + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString () + { + var header = String.Format ("{0}|{1,-5}|", _date, _level); + var method = _caller.GetMethod (); + var type = method.DeclaringType; +#if DEBUG + var lineNum = _caller.GetFileLineNumber (); + var headerAndCaller = String.Format ( + "{0}{1}.{2}:{3}|", header, type.Name, method.Name, lineNum); +#else + var headerAndCaller = String.Format ("{0}{1}.{2}|", header, type.Name, method.Name); +#endif + + var messages = _message.Replace ("\r\n", "\n").TrimEnd ('\n').Split ('\n'); + if (messages.Length <= 1) + return String.Format ("{0}{1}", headerAndCaller, _message); + + var log = new StringBuilder ( + String.Format ("{0}{1}\n", headerAndCaller, messages [0]), 64); + + var space = header.Length; + var format = String.Format ("{{0,{0}}}{{1}}\n", space); + for (var i = 1; i < messages.Length; i++) + log.AppendFormat (format, "", messages [i]); + + log.Length--; + return log.ToString (); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/LogLevel.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/LogLevel.cs new file mode 100644 index 0000000..5c9fdfe --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/LogLevel.cs @@ -0,0 +1,63 @@ +#region License +/* + * LogLevel.cs + * + * The MIT License + * + * Copyright (c) 2013-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; + +namespace WebSocketSharp +{ + /// + /// Contains the values of the logging level. + /// + public enum LogLevel + { + /// + /// Indicates the bottom logging level. + /// + Trace, + /// + /// Indicates the 2nd logging level from the bottom. + /// + Debug, + /// + /// Indicates the 3rd logging level from the bottom. + /// + Info, + /// + /// Indicates the 3rd logging level from the top. + /// + Warn, + /// + /// Indicates the 2nd logging level from the top. + /// + Error, + /// + /// Indicates the top logging level. + /// + Fatal + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Logger.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Logger.cs new file mode 100644 index 0000000..dd98e88 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Logger.cs @@ -0,0 +1,330 @@ +#region License +/* + * Logger.cs + * + * The MIT License + * + * Copyright (c) 2013-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Diagnostics; +using System.IO; + +namespace WebSocketSharp +{ + /// + /// Provides a set of methods and properties for logging. + /// + /// + /// + /// If you output a log with lower than the , + /// it cannot be outputted. + /// + /// + /// The default output action writes a log to the standard output stream and + /// the if it has a valid path. + /// + /// + /// If you would like to use the custom output action, you should set the + /// to any Action<LogData, string>. + /// + /// + public class Logger + { + #region Private Fields + + private volatile string _file; + private volatile LogLevel _level; + private Action _output; + private object _sync; + + #endregion + + #region Public Constructors + + /// + /// Initializes a new instance of the class. + /// + /// + /// This constructor initializes the current logging level with . + /// + public Logger () + : this (LogLevel.Error, null, null) + { + } + + /// + /// Initializes a new instance of the class with the specified + /// logging . + /// + /// + /// One of the enum values. + /// + public Logger (LogLevel level) + : this (level, null, null) + { + } + + /// + /// Initializes a new instance of the class with the specified + /// logging , path to the log , and + /// action. + /// + /// + /// One of the enum values. + /// + /// + /// A that represents the path to the log file. + /// + /// + /// An Action<LogData, string> delegate that references the method(s) + /// used to output a log. A parameter passed to this delegate + /// is . + /// + public Logger (LogLevel level, string file, Action output) + { + _level = level; + _file = file; + _output = output ?? defaultOutput; + _sync = new object (); + } + + #endregion + + #region Public Properties + + /// + /// Gets or sets the path to the log file. + /// + /// + /// A that represents the path to the log file if any. + /// + public string File { + get { + return _file; + } + + set { + lock (_sync) { + _file = value; + Warn ( + String.Format ("The current path to the log file has been changed to {0}.", _file)); + } + } + } + + /// + /// Gets or sets the current logging level. + /// + /// + /// A log with lower than the value of this property cannot be outputted. + /// + /// + /// One of the enum values, indicates the current logging level. + /// + public LogLevel Level { + get { + return _level; + } + + set { + lock (_sync) { + _level = value; + Warn (String.Format ("The current logging level has been changed to {0}.", _level)); + } + } + } + + /// + /// Gets or sets the current output action used to output a log. + /// + /// + /// + /// An Action<LogData, string> delegate that references the method(s) used to + /// output a log. A parameter passed to this delegate is the value of + /// the . + /// + /// + /// If the value to set is , the current output action is changed to + /// the default output action. + /// + /// + public Action Output { + get { + return _output; + } + + set { + lock (_sync) { + _output = value ?? defaultOutput; + Warn ("The current output action has been changed."); + } + } + } + + #endregion + + #region Private Methods + + private static void defaultOutput (LogData data, string path) + { + var log = data.ToString (); + Console.WriteLine (log); + if (path != null && path.Length > 0) + writeToFile (path, log); + } + + private void output (string message, LogLevel level) + { + lock (_sync) { + if (_level > level) + return; + + LogData data = null; + try { + data = new LogData (level, new StackFrame (2, true), message); + _output (data, _file); + } + catch (Exception ex) { + data = new LogData (LogLevel.Fatal, new StackFrame (0, true), ex.Message); + Console.WriteLine (data.ToString ()); + } + } + } + + private static void writeToFile (string path, string value) + { + using (var writer = new StreamWriter (path, true)) + using (var syncWriter = TextWriter.Synchronized (writer)) { + syncWriter.WriteLine (value); + } + } + + #endregion + + #region Public Methods + + /// + /// Outputs as a log with . + /// + /// + /// If the current logging level is higher than , this method + /// doesn't output as a log. + /// + /// + /// A that represents the message to output as a log. + /// + public void Debug (string message) + { + if (_level > LogLevel.Debug) + return; + + output (message, LogLevel.Debug); + } + + /// + /// Outputs as a log with . + /// + /// + /// If the current logging level is higher than , this method + /// doesn't output as a log. + /// + /// + /// A that represents the message to output as a log. + /// + public void Error (string message) + { + if (_level > LogLevel.Error) + return; + + output (message, LogLevel.Error); + } + + /// + /// Outputs as a log with . + /// + /// + /// A that represents the message to output as a log. + /// + public void Fatal (string message) + { + output (message, LogLevel.Fatal); + } + + /// + /// Outputs as a log with . + /// + /// + /// If the current logging level is higher than , this method + /// doesn't output as a log. + /// + /// + /// A that represents the message to output as a log. + /// + public void Info (string message) + { + if (_level > LogLevel.Info) + return; + + output (message, LogLevel.Info); + } + + /// + /// Outputs as a log with . + /// + /// + /// If the current logging level is higher than , this method + /// doesn't output as a log. + /// + /// + /// A that represents the message to output as a log. + /// + public void Trace (string message) + { + if (_level > LogLevel.Trace) + return; + + output (message, LogLevel.Trace); + } + + /// + /// Outputs as a log with . + /// + /// + /// If the current logging level is higher than , this method + /// doesn't output as a log. + /// + /// + /// A that represents the message to output as a log. + /// + public void Warn (string message) + { + if (_level > LogLevel.Warn) + return; + + output (message, LogLevel.Warn); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Mask.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Mask.cs new file mode 100644 index 0000000..bd23b28 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Mask.cs @@ -0,0 +1,38 @@ +#region License +/* + * Mask.cs + * + * The MIT License + * + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; + +namespace WebSocketSharp +{ + internal enum Mask : byte + { + Unmask = 0x0, + Mask = 0x1 + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/MessageEventArgs.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/MessageEventArgs.cs new file mode 100644 index 0000000..f61f8a2 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/MessageEventArgs.cs @@ -0,0 +1,127 @@ +#region License +/* + * MessageEventArgs.cs + * + * The MIT License + * + * Copyright (c) 2012-2013 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Text; + +namespace WebSocketSharp +{ + /// + /// Contains the event data associated with a event. + /// + /// + /// A event occurs when the receives + /// a text or binary data frame. + /// If you want to get the received data, you access the or + /// property. + /// + public class MessageEventArgs : EventArgs + { + #region Private Fields + + private string _data; + private Opcode _opcode; + private byte[] _rawData; + + #endregion + + #region Internal Constructors + + internal MessageEventArgs (Opcode opcode, byte[] data) + { + if ((ulong) data.LongLength > PayloadData.MaxLength) + throw new WebSocketException (CloseStatusCode.TooBig); + + _opcode = opcode; + _rawData = data; + _data = convertToString (opcode, data); + } + + internal MessageEventArgs (Opcode opcode, PayloadData payload) + { + _opcode = opcode; + _rawData = payload.ApplicationData; + _data = convertToString (opcode, _rawData); + } + + #endregion + + #region Public Properties + + /// + /// Gets the received data as a . + /// + /// + /// A that contains the received data. + /// + public string Data { + get { + return _data; + } + } + + /// + /// Gets the received data as an array of . + /// + /// + /// An array of that contains the received data. + /// + public byte [] RawData { + get { + return _rawData; + } + } + + /// + /// Gets the type of the received data. + /// + /// + /// One of the values, indicates the type of the received data. + /// + public Opcode Type { + get { + return _opcode; + } + } + + #endregion + + #region Private Methods + + private static string convertToString (Opcode opcode, byte [] data) + { + return data.LongLength == 0 + ? String.Empty + : opcode == Opcode.Text + ? Encoding.UTF8.GetString (data) + : opcode.ToString (); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/AuthenticationBase.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/AuthenticationBase.cs new file mode 100644 index 0000000..1077504 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/AuthenticationBase.cs @@ -0,0 +1,151 @@ +#region License +/* + * AuthenticationBase.cs + * + * The MIT License + * + * Copyright (c) 2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Collections.Specialized; +using System.Text; + +namespace WebSocketSharp.Net +{ + internal abstract class AuthenticationBase + { + #region Private Fields + + private AuthenticationSchemes _scheme; + + #endregion + + #region Internal Fields + + internal NameValueCollection Parameters; + + #endregion + + #region Protected Constructors + + protected AuthenticationBase (AuthenticationSchemes scheme, NameValueCollection parameters) + { + _scheme = scheme; + Parameters = parameters; + } + + #endregion + + #region Public Properties + + public string Algorithm { + get { + return Parameters["algorithm"]; + } + } + + public string Nonce { + get { + return Parameters["nonce"]; + } + } + + public string Opaque { + get { + return Parameters["opaque"]; + } + } + + public string Qop { + get { + return Parameters["qop"]; + } + } + + public string Realm { + get { + return Parameters["realm"]; + } + } + + public AuthenticationSchemes Scheme { + get { + return _scheme; + } + } + + #endregion + + #region Internal Methods + + internal static string CreateNonceValue () + { + var src = new byte[16]; + var rand = new Random (); + rand.NextBytes (src); + + var res = new StringBuilder (32); + foreach (var b in src) + res.Append (b.ToString ("x2")); + + return res.ToString (); + } + + internal static NameValueCollection ParseParameters (string value) + { + var res = new NameValueCollection (); + foreach (var param in value.SplitHeaderValue (',')) { + var i = param.IndexOf ('='); + var name = i > 0 ? param.Substring (0, i).Trim () : null; + var val = i < 0 + ? param.Trim ().Trim ('"') + : i < param.Length - 1 + ? param.Substring (i + 1).Trim ().Trim ('"') + : String.Empty; + + res.Add (name, val); + } + + return res; + } + + internal abstract string ToBasicString (); + + internal abstract string ToDigestString (); + + #endregion + + #region Public Methods + + public override string ToString () + { + return _scheme == AuthenticationSchemes.Basic + ? ToBasicString () + : _scheme == AuthenticationSchemes.Digest + ? ToDigestString () + : String.Empty; + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/AuthenticationChallenge.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/AuthenticationChallenge.cs new file mode 100644 index 0000000..3472204 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/AuthenticationChallenge.cs @@ -0,0 +1,146 @@ +#region License +/* + * AuthenticationChallenge.cs + * + * The MIT License + * + * Copyright (c) 2013-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Collections.Specialized; +using System.Text; + +namespace WebSocketSharp.Net +{ + internal class AuthenticationChallenge : AuthenticationBase + { + #region Private Constructors + + private AuthenticationChallenge (AuthenticationSchemes scheme, NameValueCollection parameters) + : base (scheme, parameters) + { + } + + #endregion + + #region Internal Constructors + + internal AuthenticationChallenge (AuthenticationSchemes scheme, string realm) + : base (scheme, new NameValueCollection ()) + { + Parameters["realm"] = realm; + if (scheme == AuthenticationSchemes.Digest) { + Parameters["nonce"] = CreateNonceValue (); + Parameters["algorithm"] = "MD5"; + Parameters["qop"] = "auth"; + } + } + + #endregion + + #region Public Properties + + public string Domain { + get { + return Parameters["domain"]; + } + } + + public string Stale { + get { + return Parameters["stale"]; + } + } + + #endregion + + #region Internal Methods + + internal static AuthenticationChallenge CreateBasicChallenge (string realm) + { + return new AuthenticationChallenge (AuthenticationSchemes.Basic, realm); + } + + internal static AuthenticationChallenge CreateDigestChallenge (string realm) + { + return new AuthenticationChallenge (AuthenticationSchemes.Digest, realm); + } + + internal static AuthenticationChallenge Parse (string value) + { + var chal = value.Split (new[] { ' ' }, 2); + if (chal.Length != 2) + return null; + + var schm = chal[0].ToLower (); + return schm == "basic" + ? new AuthenticationChallenge ( + AuthenticationSchemes.Basic, ParseParameters (chal[1])) + : schm == "digest" + ? new AuthenticationChallenge ( + AuthenticationSchemes.Digest, ParseParameters (chal[1])) + : null; + } + + internal override string ToBasicString () + { + return String.Format ("Basic realm=\"{0}\"", Parameters["realm"]); + } + + internal override string ToDigestString () + { + var output = new StringBuilder (128); + + var domain = Parameters["domain"]; + if (domain != null) + output.AppendFormat ( + "Digest realm=\"{0}\", domain=\"{1}\", nonce=\"{2}\"", + Parameters["realm"], + domain, + Parameters["nonce"]); + else + output.AppendFormat ( + "Digest realm=\"{0}\", nonce=\"{1}\"", Parameters["realm"], Parameters["nonce"]); + + var opaque = Parameters["opaque"]; + if (opaque != null) + output.AppendFormat (", opaque=\"{0}\"", opaque); + + var stale = Parameters["stale"]; + if (stale != null) + output.AppendFormat (", stale={0}", stale); + + var algo = Parameters["algorithm"]; + if (algo != null) + output.AppendFormat (", algorithm={0}", algo); + + var qop = Parameters["qop"]; + if (qop != null) + output.AppendFormat (", qop=\"{0}\"", qop); + + return output.ToString (); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/AuthenticationResponse.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/AuthenticationResponse.cs new file mode 100644 index 0000000..cc49b37 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/AuthenticationResponse.cs @@ -0,0 +1,323 @@ +#region License +/* + * AuthenticationResponse.cs + * + * ParseBasicCredentials is derived from System.Net.HttpListenerContext.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2013-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Collections.Specialized; +using System.Security.Cryptography; +using System.Security.Principal; +using System.Text; + +namespace WebSocketSharp.Net +{ + internal class AuthenticationResponse : AuthenticationBase + { + #region Private Fields + + private uint _nonceCount; + + #endregion + + #region Private Constructors + + private AuthenticationResponse (AuthenticationSchemes scheme, NameValueCollection parameters) + : base (scheme, parameters) + { + } + + #endregion + + #region Internal Constructors + + internal AuthenticationResponse (NetworkCredential credentials) + : this (AuthenticationSchemes.Basic, new NameValueCollection (), credentials, 0) + { + } + + internal AuthenticationResponse ( + AuthenticationChallenge challenge, NetworkCredential credentials, uint nonceCount) + : this (challenge.Scheme, challenge.Parameters, credentials, nonceCount) + { + } + + internal AuthenticationResponse ( + AuthenticationSchemes scheme, + NameValueCollection parameters, + NetworkCredential credentials, + uint nonceCount) + : base (scheme, parameters) + { + Parameters["username"] = credentials.UserName; + Parameters["password"] = credentials.Password; + Parameters["uri"] = credentials.Domain; + _nonceCount = nonceCount; + if (scheme == AuthenticationSchemes.Digest) + initAsDigest (); + } + + #endregion + + #region Internal Properties + + internal uint NonceCount { + get { + return _nonceCount < UInt32.MaxValue + ? _nonceCount + : 0; + } + } + + #endregion + + #region Public Properties + + public string Cnonce { + get { + return Parameters["cnonce"]; + } + } + + public string Nc { + get { + return Parameters["nc"]; + } + } + + public string Password { + get { + return Parameters["password"]; + } + } + + public string Response { + get { + return Parameters["response"]; + } + } + + public string Uri { + get { + return Parameters["uri"]; + } + } + + public string UserName { + get { + return Parameters["username"]; + } + } + + #endregion + + #region Private Methods + + private static string createA1 (string username, string password, string realm) + { + return String.Format ("{0}:{1}:{2}", username, realm, password); + } + + private static string createA1 ( + string username, string password, string realm, string nonce, string cnonce) + { + return String.Format ( + "{0}:{1}:{2}", hash (createA1 (username, password, realm)), nonce, cnonce); + } + + private static string createA2 (string method, string uri) + { + return String.Format ("{0}:{1}", method, uri); + } + + private static string createA2 (string method, string uri, string entity) + { + return String.Format ("{0}:{1}:{2}", method, uri, hash (entity)); + } + + private static string hash (string value) + { + var src = Encoding.UTF8.GetBytes (value); + var md5 = MD5.Create (); + var hashed = md5.ComputeHash (src); + + var res = new StringBuilder (64); + foreach (var b in hashed) + res.Append (b.ToString ("x2")); + + return res.ToString (); + } + + private void initAsDigest () + { + var qops = Parameters["qop"]; + if (qops != null) { + if (qops.Split (',').Contains (qop => qop.Trim ().ToLower () == "auth")) { + Parameters["qop"] = "auth"; + Parameters["cnonce"] = CreateNonceValue (); + Parameters["nc"] = String.Format ("{0:x8}", ++_nonceCount); + } + else { + Parameters["qop"] = null; + } + } + + Parameters["method"] = "GET"; + Parameters["response"] = CreateRequestDigest (Parameters); + } + + #endregion + + #region Internal Methods + + internal static string CreateRequestDigest (NameValueCollection parameters) + { + var user = parameters["username"]; + var pass = parameters["password"]; + var realm = parameters["realm"]; + var nonce = parameters["nonce"]; + var uri = parameters["uri"]; + var algo = parameters["algorithm"]; + var qop = parameters["qop"]; + var cnonce = parameters["cnonce"]; + var nc = parameters["nc"]; + var method = parameters["method"]; + + var a1 = algo != null && algo.ToLower () == "md5-sess" + ? createA1 (user, pass, realm, nonce, cnonce) + : createA1 (user, pass, realm); + + var a2 = qop != null && qop.ToLower () == "auth-int" + ? createA2 (method, uri, parameters["entity"]) + : createA2 (method, uri); + + var secret = hash (a1); + var data = qop != null + ? String.Format ("{0}:{1}:{2}:{3}:{4}", nonce, nc, cnonce, qop, hash (a2)) + : String.Format ("{0}:{1}", nonce, hash (a2)); + + return hash (String.Format ("{0}:{1}", secret, data)); + } + + internal static AuthenticationResponse Parse (string value) + { + try { + var cred = value.Split (new[] { ' ' }, 2); + if (cred.Length != 2) + return null; + + var schm = cred[0].ToLower (); + return schm == "basic" + ? new AuthenticationResponse ( + AuthenticationSchemes.Basic, ParseBasicCredentials (cred[1])) + : schm == "digest" + ? new AuthenticationResponse ( + AuthenticationSchemes.Digest, ParseParameters (cred[1])) + : null; + } + catch { + } + + return null; + } + + internal static NameValueCollection ParseBasicCredentials (string value) + { + // Decode the basic-credentials (a Base64 encoded string). + var userPass = Encoding.Default.GetString (Convert.FromBase64String (value)); + + // The format is [\]:. + var i = userPass.IndexOf (':'); + var user = userPass.Substring (0, i); + var pass = i < userPass.Length - 1 ? userPass.Substring (i + 1) : String.Empty; + + // Check if 'domain' exists. + i = user.IndexOf ('\\'); + if (i > -1) + user = user.Substring (i + 1); + + var res = new NameValueCollection (); + res["username"] = user; + res["password"] = pass; + + return res; + } + + internal override string ToBasicString () + { + var userPass = String.Format ("{0}:{1}", Parameters["username"], Parameters["password"]); + var cred = Convert.ToBase64String (Encoding.UTF8.GetBytes (userPass)); + + return "Basic " + cred; + } + + internal override string ToDigestString () + { + var output = new StringBuilder (256); + output.AppendFormat ( + "Digest username=\"{0}\", realm=\"{1}\", nonce=\"{2}\", uri=\"{3}\", response=\"{4}\"", + Parameters["username"], + Parameters["realm"], + Parameters["nonce"], + Parameters["uri"], + Parameters["response"]); + + var opaque = Parameters["opaque"]; + if (opaque != null) + output.AppendFormat (", opaque=\"{0}\"", opaque); + + var algo = Parameters["algorithm"]; + if (algo != null) + output.AppendFormat (", algorithm={0}", algo); + + var qop = Parameters["qop"]; + if (qop != null) + output.AppendFormat ( + ", qop={0}, cnonce=\"{1}\", nc={2}", qop, Parameters["cnonce"], Parameters["nc"]); + + return output.ToString (); + } + + #endregion + + #region Public Methods + + public IIdentity ToIdentity () + { + var schm = Scheme; + return schm == AuthenticationSchemes.Basic + ? new HttpBasicIdentity (Parameters["username"], Parameters["password"]) as IIdentity + : schm == AuthenticationSchemes.Digest + ? new HttpDigestIdentity (Parameters) + : null; + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/AuthenticationSchemes.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/AuthenticationSchemes.cs new file mode 100644 index 0000000..af0efb1 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/AuthenticationSchemes.cs @@ -0,0 +1,67 @@ +#region License +/* + * AuthenticationSchemes.cs + * + * This code is derived from System.Net.AuthenticationSchemes.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Atsushi Enomoto + */ +#endregion + +using System; + +namespace WebSocketSharp.Net +{ + /// + /// Contains the values of the schemes for authentication. + /// + [Flags] + public enum AuthenticationSchemes + { + /// + /// Indicates that no authentication is allowed. + /// + None, + /// + /// Indicates digest authentication. + /// + Digest = 1, + /// + /// Indicates basic authentication. + /// + Basic = 8, + /// + /// Indicates anonymous authentication. + /// + Anonymous = 0x8000 + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/Chunk.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/Chunk.cs new file mode 100644 index 0000000..efa165c --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/Chunk.cs @@ -0,0 +1,91 @@ +#region License +/* + * Chunk.cs + * + * This code is derived from System.Net.ChunkStream.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2003 Ximian, Inc (http://www.ximian.com) + * Copyright (c) 2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; + +namespace WebSocketSharp.Net +{ + internal class Chunk + { + #region Private Fields + + private byte [] _data; + private int _offset; + + #endregion + + #region Public Constructors + + public Chunk (byte [] data) + { + _data = data; + } + + #endregion + + #region Public Properties + + public int ReadLeft { + get { + return _data.Length - _offset; + } + } + + #endregion + + #region Public Methods + + public int Read (byte [] buffer, int offset, int size) + { + var left = _data.Length - _offset; + if (left == 0) + return left; + + if (size > left) + size = left; + + Buffer.BlockCopy (_data, _offset, buffer, offset, size); + _offset += size; + + return size; + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/ChunkStream.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/ChunkStream.cs new file mode 100644 index 0000000..0d521ed --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/ChunkStream.cs @@ -0,0 +1,368 @@ +#region License +/* + * ChunkStream.cs + * + * This code is derived from System.Net.ChunkStream.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2003 Ximian, Inc (http://www.ximian.com) + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Net; +using System.Text; + +namespace WebSocketSharp.Net +{ + internal class ChunkStream + { + #region Private Fields + + private int _chunkRead; + private int _chunkSize; + private List _chunks; + private bool _gotit; + private WebHeaderCollection _headers; + private StringBuilder _saved; + private bool _sawCR; + private InputChunkState _state; + private int _trailerState; + + #endregion + + #region Public Constructors + + public ChunkStream (byte [] buffer, int offset, int size, WebHeaderCollection headers) + : this (headers) + { + Write (buffer, offset, size); + } + + public ChunkStream (WebHeaderCollection headers) + { + _headers = headers; + _chunkSize = -1; + _chunks = new List (); + _saved = new StringBuilder (); + } + + #endregion + + #region Internal Properties + + internal WebHeaderCollection Headers { + get { + return _headers; + } + } + + #endregion + + #region Public Properties + + public int ChunkLeft { + get { + return _chunkSize - _chunkRead; + } + } + + public bool WantMore { + get { + return _chunkRead != _chunkSize || _chunkSize != 0 || _state != InputChunkState.None; + } + } + + #endregion + + #region Private Methods + + private InputChunkState readCRLF (byte [] buffer, ref int offset, int size) + { + if (!_sawCR) { + if ((char) buffer [offset++] != '\r') + throwProtocolViolation ("Expecting \\r."); + + _sawCR = true; + if (offset == size) + return InputChunkState.BodyFinished; + } + + if ((char) buffer [offset++] != '\n') + throwProtocolViolation ("Expecting \\n."); + + return InputChunkState.None; + } + + private int readFromChunks (byte [] buffer, int offset, int size) + { + var count = _chunks.Count; + var nread = 0; + for (int i = 0; i < count; i++) { + var chunk = _chunks [i]; + if (chunk == null) + continue; + + if (chunk.ReadLeft == 0) { + _chunks [i] = null; + continue; + } + + nread += chunk.Read (buffer, offset + nread, size - nread); + if (nread == size) + break; + } + + return nread; + } + + private InputChunkState readTrailer (byte [] buffer, ref int offset, int size) + { + var c = '\0'; + + // Short path + if (_trailerState == 2 && (char) buffer [offset] == '\r' && _saved.Length == 0) { + offset++; + if (offset < size && (char) buffer [offset] == '\n') { + offset++; + return InputChunkState.None; + } + + offset--; + } + + var st = _trailerState; + var stString = "\r\n\r"; + while (offset < size && st < 4) { + c = (char) buffer [offset++]; + if ((st == 0 || st == 2) && c == '\r') { + st++; + continue; + } + + if ((st == 1 || st == 3) && c == '\n') { + st++; + continue; + } + + if (st > 0) { + _saved.Append (stString.Substring (0, _saved.Length == 0 ? st - 2 : st)); + st = 0; + if (_saved.Length > 4196) + throwProtocolViolation ("Error reading trailer (too long)."); + } + } + + if (st < 4) { + _trailerState = st; + if (offset < size) + throwProtocolViolation ("Error reading trailer."); + + return InputChunkState.Trailer; + } + + var reader = new StringReader (_saved.ToString ()); + string line; + while ((line = reader.ReadLine ()) != null && line != "") + _headers.Add (line); + + return InputChunkState.None; + } + + private static string removeChunkExtension (string input) + { + var idx = input.IndexOf (';'); + return idx > -1 + ? input.Substring (0, idx) + : input; + } + + private InputChunkState setChunkSize (byte [] buffer, ref int offset, int size) + { + var c = '\0'; + while (offset < size) { + c = (char) buffer [offset++]; + if (c == '\r') { + if (_sawCR) + throwProtocolViolation ("2 CR found."); + + _sawCR = true; + continue; + } + + if (_sawCR && c == '\n') + break; + + if (c == ' ') + _gotit = true; + + if (!_gotit) + _saved.Append (c); + + if (_saved.Length > 20) + throwProtocolViolation ("Chunk size too long."); + } + + if (!_sawCR || c != '\n') { + if (offset < size) + throwProtocolViolation ("Missing \\n."); + + try { + if (_saved.Length > 0) + _chunkSize = Int32.Parse ( + removeChunkExtension (_saved.ToString ()), NumberStyles.HexNumber); + } + catch { + throwProtocolViolation ("Cannot parse chunk size."); + } + + return InputChunkState.None; + } + + _chunkRead = 0; + try { + _chunkSize = Int32.Parse ( + removeChunkExtension (_saved.ToString ()), NumberStyles.HexNumber); + } + catch { + throwProtocolViolation ("Cannot parse chunk size."); + } + + if (_chunkSize == 0) { + _trailerState = 2; + return InputChunkState.Trailer; + } + + return InputChunkState.Body; + } + + private static void throwProtocolViolation (string message) + { + var ex = new WebException (message, null, WebExceptionStatus.ServerProtocolViolation, null); + throw ex; + } + + private void write (byte [] buffer, ref int offset, int size) + { + if (_state == InputChunkState.None) { + _state = setChunkSize (buffer, ref offset, size); + if (_state == InputChunkState.None) + return; + + _saved.Length = 0; + _sawCR = false; + _gotit = false; + } + + if (_state == InputChunkState.Body && offset < size) { + _state = writeBody (buffer, ref offset, size); + if (_state == InputChunkState.Body) + return; + } + + if (_state == InputChunkState.BodyFinished && offset < size) { + _state = readCRLF (buffer, ref offset, size); + if (_state == InputChunkState.BodyFinished) + return; + + _sawCR = false; + } + + if (_state == InputChunkState.Trailer && offset < size) { + _state = readTrailer (buffer, ref offset, size); + if (_state == InputChunkState.Trailer) + return; + + _saved.Length = 0; + _sawCR = false; + _gotit = false; + } + + if (offset < size) + write (buffer, ref offset, size); + } + + private InputChunkState writeBody (byte [] buffer, ref int offset, int size) + { + if (_chunkSize == 0) + return InputChunkState.BodyFinished; + + var diff = size - offset; + if (diff + _chunkRead > _chunkSize) + diff = _chunkSize - _chunkRead; + + var body = new byte [diff]; + Buffer.BlockCopy (buffer, offset, body, 0, diff); + _chunks.Add (new Chunk (body)); + + offset += diff; + _chunkRead += diff; + + return _chunkRead == _chunkSize + ? InputChunkState.BodyFinished + : InputChunkState.Body; + } + + #endregion + + #region Public Methods + + public int Read (byte [] buffer, int offset, int size) + { + return readFromChunks (buffer, offset, size); + } + + public void ResetBuffer () + { + _chunkSize = -1; + _chunkRead = 0; + _chunks.Clear (); + } + + public void Write (byte [] buffer, int offset, int size) + { + write (buffer, ref offset, size); + } + + public void WriteAndReadBack (byte [] buffer, int offset, int size, ref int read) + { + if (offset + read > 0) + Write (buffer, offset, offset + read); + + read = readFromChunks (buffer, offset, size); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/ChunkedRequestStream.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/ChunkedRequestStream.cs new file mode 100644 index 0000000..7a7a36d --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/ChunkedRequestStream.cs @@ -0,0 +1,212 @@ +#region License +/* + * ChunkedRequestStream.cs + * + * This code is derived from System.Net.ChunkedInputStream.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; +using System.IO; + +namespace WebSocketSharp.Net +{ + internal class ChunkedRequestStream : RequestStream + { + #region Private Const Fields + + private const int _bufferSize = 8192; + + #endregion + + #region Private Fields + + private HttpListenerContext _context; + private ChunkStream _decoder; + private bool _disposed; + private bool _noMoreData; + + #endregion + + #region Public Constructors + + public ChunkedRequestStream ( + HttpListenerContext context, Stream stream, byte [] buffer, int offset, int length) + : base (stream, buffer, offset, length) + { + _context = context; + _decoder = new ChunkStream ((WebHeaderCollection) context.Request.Headers); + } + + #endregion + + #region Public Properties + + public ChunkStream Decoder { + get { + return _decoder; + } + + set { + _decoder = value; + } + } + + #endregion + + #region Private Methods + + private void onRead (IAsyncResult asyncResult) + { + var readState = (ReadBufferState) asyncResult.AsyncState; + var ares = readState.AsyncResult; + try { + var nread = base.EndRead (asyncResult); + _decoder.Write (ares.Buffer, ares.Offset, nread); + nread = _decoder.Read (readState.Buffer, readState.Offset, readState.Count); + readState.Offset += nread; + readState.Count -= nread; + if (readState.Count == 0 || !_decoder.WantMore || nread == 0) { + _noMoreData = !_decoder.WantMore && nread == 0; + ares.Count = readState.InitialCount - readState.Count; + ares.Complete (); + + return; + } + + ares.Offset = 0; + ares.Count = Math.Min (_bufferSize, _decoder.ChunkLeft + 6); + base.BeginRead (ares.Buffer, ares.Offset, ares.Count, onRead, readState); + } + catch (Exception ex) { + _context.Connection.SendError (ex.Message, 400); + ares.Complete (ex); + } + } + + #endregion + + #region Public Methods + + public override IAsyncResult BeginRead ( + byte [] buffer, int offset, int count, AsyncCallback callback, object state) + { + if (_disposed) + throw new ObjectDisposedException (GetType ().ToString ()); + + if (buffer == null) + throw new ArgumentNullException ("buffer"); + + var len = buffer.Length; + if (offset < 0 || offset > len) + throw new ArgumentOutOfRangeException ("'offset' exceeds the size of buffer."); + + if (count < 0 || offset > len - count) + throw new ArgumentOutOfRangeException ("'offset' + 'count' exceeds the size of buffer."); + + var ares = new HttpStreamAsyncResult (callback, state); + if (_noMoreData) { + ares.Complete (); + return ares; + } + + var nread = _decoder.Read (buffer, offset, count); + offset += nread; + count -= nread; + if (count == 0) { + // Got all we wanted, no need to bother the decoder yet. + ares.Count = nread; + ares.Complete (); + + return ares; + } + + if (!_decoder.WantMore) { + _noMoreData = nread == 0; + ares.Count = nread; + ares.Complete (); + + return ares; + } + + ares.Buffer = new byte [_bufferSize]; + ares.Offset = 0; + ares.Count = _bufferSize; + + var readState = new ReadBufferState (buffer, offset, count, ares); + readState.InitialCount += nread; + base.BeginRead (ares.Buffer, ares.Offset, ares.Count, onRead, readState); + + return ares; + } + + public override void Close () + { + if (_disposed) + return; + + _disposed = true; + base.Close (); + } + + public override int EndRead (IAsyncResult asyncResult) + { + if (_disposed) + throw new ObjectDisposedException (GetType ().ToString ()); + + if (asyncResult == null) + throw new ArgumentNullException ("asyncResult"); + + var ares = asyncResult as HttpStreamAsyncResult; + if (ares == null) + throw new ArgumentException ("Wrong IAsyncResult.", "asyncResult"); + + if (!ares.IsCompleted) + ares.AsyncWaitHandle.WaitOne (); + + if (ares.Error != null) + throw new HttpListenerException (400, "I/O operation aborted."); + + return ares.Count; + } + + public override int Read (byte [] buffer, int offset, int count) + { + var ares = BeginRead (buffer, offset, count, null, null); + return EndRead (ares); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/Cookie.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/Cookie.cs new file mode 100644 index 0000000..b73a5cd --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/Cookie.cs @@ -0,0 +1,813 @@ +#region License +/* + * Cookie.cs + * + * This code is derived from System.Net.Cookie.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2004,2009 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Lawrence Pit + * - Gonzalo Paniagua Javier + * - Daniel Nauck + * - Sebastien Pouliot + */ +#endregion + +using System; +using System.Globalization; +using System.Text; + +namespace WebSocketSharp.Net +{ + /// + /// Provides a set of methods and properties used to manage an HTTP Cookie. + /// + /// + /// + /// The Cookie class supports the following cookie formats: + /// Netscape specification, + /// RFC 2109, and + /// RFC 2965 + /// + /// + /// The Cookie class cannot be inherited. + /// + /// + [Serializable] + public sealed class Cookie + { + #region Private Static Fields + + private static char [] _reservedCharsForName = { ' ', '=', ';', ',', '\n', '\r', '\t' }; + private static char [] _reservedCharsForValue = { ';', ',' }; + + #endregion + + #region Private Fields + + private string _comment; + private Uri _commentUri; + private bool _discard; + private string _domain; + private DateTime _expires; + private bool _httpOnly; + private string _name; + private string _path; + private string _port; + private int [] _ports; + private bool _secure; + private DateTime _timestamp; + private string _value; + private int _version; + + #endregion + + #region Public Constructors + + /// + /// Initializes a new instance of the class. + /// + public Cookie () + { + _comment = String.Empty; + _domain = String.Empty; + _expires = DateTime.MinValue; + _name = String.Empty; + _path = String.Empty; + _port = String.Empty; + _ports = new int [0]; + _timestamp = DateTime.Now; + _value = String.Empty; + _version = 0; + } + + /// + /// Initializes a new instance of the class with the specified + /// and . + /// + /// + /// A that represents the Name of the cookie. + /// + /// + /// A that represents the Value of the cookie. + /// + /// + /// + /// is or empty. + /// + /// + /// - or - + /// + /// + /// contains an invalid character. + /// + /// + /// - or - + /// + /// + /// is . + /// + /// + /// - or - + /// + /// + /// contains a string not enclosed in double quotes + /// that contains an invalid character. + /// + /// + public Cookie (string name, string value) + : this () + { + Name = name; + Value = value; + } + + /// + /// Initializes a new instance of the class with the specified + /// , , and . + /// + /// + /// A that represents the Name of the cookie. + /// + /// + /// A that represents the Value of the cookie. + /// + /// + /// A that represents the value of the Path attribute of the cookie. + /// + /// + /// + /// is or empty. + /// + /// + /// - or - + /// + /// + /// contains an invalid character. + /// + /// + /// - or - + /// + /// + /// is . + /// + /// + /// - or - + /// + /// + /// contains a string not enclosed in double quotes + /// that contains an invalid character. + /// + /// + public Cookie (string name, string value, string path) + : this (name, value) + { + Path = path; + } + + /// + /// Initializes a new instance of the class with the specified + /// , , , and + /// . + /// + /// + /// A that represents the Name of the cookie. + /// + /// + /// A that represents the Value of the cookie. + /// + /// + /// A that represents the value of the Path attribute of the cookie. + /// + /// + /// A that represents the value of the Domain attribute of the cookie. + /// + /// + /// + /// is or empty. + /// + /// + /// - or - + /// + /// + /// contains an invalid character. + /// + /// + /// - or - + /// + /// + /// is . + /// + /// + /// - or - + /// + /// + /// contains a string not enclosed in double quotes + /// that contains an invalid character. + /// + /// + public Cookie (string name, string value, string path, string domain) + : this (name, value, path) + { + Domain = domain; + } + + #endregion + + #region Internal Properties + + internal bool ExactDomain { + get; set; + } + + internal int MaxAge { + get { + if (_expires == DateTime.MinValue) + return 0; + + var expires = _expires.Kind != DateTimeKind.Local + ? _expires.ToLocalTime () + : _expires; + + var span = expires - DateTime.Now; + return span > TimeSpan.Zero + ? (int) span.TotalSeconds + : 0; + } + } + + internal int [] Ports { + get { + return _ports; + } + } + + #endregion + + #region Public Properties + + /// + /// Gets or sets the value of the Comment attribute of the cookie. + /// + /// + /// A that represents the comment to document intended use of the cookie. + /// + public string Comment { + get { + return _comment; + } + + set { + _comment = value ?? String.Empty; + } + } + + /// + /// Gets or sets the value of the CommentURL attribute of the cookie. + /// + /// + /// A that represents the URI that provides the comment to document intended + /// use of the cookie. + /// + public Uri CommentUri { + get { + return _commentUri; + } + + set { + _commentUri = value; + } + } + + /// + /// Gets or sets a value indicating whether the client discards the cookie unconditionally + /// when the client terminates. + /// + /// + /// true if the client discards the cookie unconditionally when the client terminates; + /// otherwise, false. The default value is false. + /// + public bool Discard { + get { + return _discard; + } + + set { + _discard = value; + } + } + + /// + /// Gets or sets the value of the Domain attribute of the cookie. + /// + /// + /// A that represents the URI for which the cookie is valid. + /// + public string Domain { + get { + return _domain; + } + + set { + if (value.IsNullOrEmpty ()) { + _domain = String.Empty; + ExactDomain = true; + } + else { + _domain = value; + ExactDomain = value [0] != '.'; + } + } + } + + /// + /// Gets or sets a value indicating whether the cookie has expired. + /// + /// + /// true if the cookie has expired; otherwise, false. + /// The default value is false. + /// + public bool Expired { + get { + return _expires != DateTime.MinValue && _expires <= DateTime.Now; + } + + set { + _expires = value ? DateTime.Now : DateTime.MinValue; + } + } + + /// + /// Gets or sets the value of the Expires attribute of the cookie. + /// + /// + /// A that represents the date and time at which the cookie expires. + /// The default value is . + /// + public DateTime Expires { + get { + return _expires; + } + + set { + _expires = value; + } + } + + /// + /// Gets or sets a value indicating whether non-HTTP APIs can access the cookie. + /// + /// + /// true if non-HTTP APIs cannot access the cookie; otherwise, false. + /// The default value is false. + /// + public bool HttpOnly { + get { + return _httpOnly; + } + + set { + _httpOnly = value; + } + } + + /// + /// Gets or sets the Name of the cookie. + /// + /// + /// A that represents the Name of the cookie. + /// + /// + /// + /// The value specified for a set operation is or empty. + /// + /// + /// - or - + /// + /// + /// The value specified for a set operation contains an invalid character. + /// + /// + public string Name { + get { + return _name; + } + + set { + string msg; + if (!canSetName (value, out msg)) + throw new CookieException (msg); + + _name = value; + } + } + + /// + /// Gets or sets the value of the Path attribute of the cookie. + /// + /// + /// A that represents the subset of URI on the origin server + /// to which the cookie applies. + /// + public string Path { + get { + return _path; + } + + set { + _path = value ?? String.Empty; + } + } + + /// + /// Gets or sets the value of the Port attribute of the cookie. + /// + /// + /// A that represents the list of TCP ports to which the cookie applies. + /// + /// + /// The value specified for a set operation isn't enclosed in double quotes or + /// couldn't be parsed. + /// + public string Port { + get { + return _port; + } + + set { + if (value.IsNullOrEmpty ()) { + _port = String.Empty; + _ports = new int [0]; + + return; + } + + if (!value.IsEnclosedIn ('"')) + throw new CookieException ( + "The value of Port attribute must be enclosed in double quotes."); + + string error; + if (!tryCreatePorts (value, out _ports, out error)) + throw new CookieException ( + String.Format ( + "The value specified for a Port attribute contains an invalid value: {0}", error)); + + _port = value; + } + } + + /// + /// Gets or sets a value indicating whether the security level of the cookie is secure. + /// + /// + /// When this property is true, the cookie may be included in the HTTP request + /// only if the request is transmitted over the HTTPS. + /// + /// + /// true if the security level of the cookie is secure; otherwise, false. + /// The default value is false. + /// + public bool Secure { + get { + return _secure; + } + + set { + _secure = value; + } + } + + /// + /// Gets the time when the cookie was issued. + /// + /// + /// A that represents the time when the cookie was issued. + /// + public DateTime TimeStamp { + get { + return _timestamp; + } + } + + /// + /// Gets or sets the Value of the cookie. + /// + /// + /// A that represents the Value of the cookie. + /// + /// + /// + /// The value specified for a set operation is . + /// + /// + /// - or - + /// + /// + /// The value specified for a set operation contains a string not enclosed in double quotes + /// that contains an invalid character. + /// + /// + public string Value { + get { + return _value; + } + + set { + string msg; + if (!canSetValue (value, out msg)) + throw new CookieException (msg); + + _value = value.Length > 0 ? value : "\"\""; + } + } + + /// + /// Gets or sets the value of the Version attribute of the cookie. + /// + /// + /// An that represents the version of the HTTP state management + /// to which the cookie conforms. + /// + /// + /// The value specified for a set operation isn't 0 or 1. + /// + public int Version { + get { + return _version; + } + + set { + if (value < 0 || value > 1) + throw new ArgumentOutOfRangeException ("value", "Must be 0 or 1."); + + _version = value; + } + } + + #endregion + + #region Private Methods + + private static bool canSetName (string name, out string message) + { + if (name.IsNullOrEmpty ()) { + message = "Name must not be null or empty."; + return false; + } + + if (name [0] == '$' || name.Contains (_reservedCharsForName)) { + message = "The value specified for a Name contains an invalid character."; + return false; + } + + message = String.Empty; + return true; + } + + private static bool canSetValue (string value, out string message) + { + if (value == null) { + message = "Value must not be null."; + return false; + } + + if (value.Contains (_reservedCharsForValue) && !value.IsEnclosedIn ('"')) { + message = "The value specified for a Value contains an invalid character."; + return false; + } + + message = String.Empty; + return true; + } + + private static int hash (int i, int j, int k, int l, int m) + { + return i ^ + (j << 13 | j >> 19) ^ + (k << 26 | k >> 6) ^ + (l << 7 | l >> 25) ^ + (m << 20 | m >> 12); + } + + private string toResponseStringVersion0 () + { + var result = new StringBuilder (64); + result.AppendFormat ("{0}={1}", _name, _value); + + if (_expires != DateTime.MinValue) + result.AppendFormat ( + "; Expires={0}", + _expires.ToUniversalTime ().ToString ( + "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'", + CultureInfo.CreateSpecificCulture ("en-US"))); + + if (!_path.IsNullOrEmpty ()) + result.AppendFormat ("; Path={0}", _path); + + if (!_domain.IsNullOrEmpty ()) + result.AppendFormat ("; Domain={0}", _domain); + + if (_secure) + result.Append ("; Secure"); + + if (_httpOnly) + result.Append ("; HttpOnly"); + + return result.ToString (); + } + + private string toResponseStringVersion1 () + { + var result = new StringBuilder (64); + result.AppendFormat ("{0}={1}; Version={2}", _name, _value, _version); + + if (_expires != DateTime.MinValue) + result.AppendFormat ("; Max-Age={0}", MaxAge); + + if (!_path.IsNullOrEmpty ()) + result.AppendFormat ("; Path={0}", _path); + + if (!_domain.IsNullOrEmpty ()) + result.AppendFormat ("; Domain={0}", _domain); + + if (!_port.IsNullOrEmpty ()) { + if (_port == "\"\"") + result.Append ("; Port"); + else + result.AppendFormat ("; Port={0}", _port); + } + + if (!_comment.IsNullOrEmpty ()) + result.AppendFormat ("; Comment={0}", _comment.UrlEncode ()); + + if (_commentUri != null) + result.AppendFormat ("; CommentURL={0}", _commentUri.OriginalString.Quote ()); + + if (_discard) + result.Append ("; Discard"); + + if (_secure) + result.Append ("; Secure"); + + return result.ToString (); + } + + private static bool tryCreatePorts (string value, out int [] result, out string parseError) + { + var ports = value.Trim ('"').Split (','); + var tmp = new int [ports.Length]; + for (int i = 0; i < ports.Length; i++) { + tmp [i] = int.MinValue; + var port = ports [i].Trim (); + if (port.Length == 0) + continue; + + if (!int.TryParse (port, out tmp [i])) { + result = new int [0]; + parseError = port; + + return false; + } + } + + result = tmp; + parseError = String.Empty; + + return true; + } + + #endregion + + #region Internal Methods + + // From client to server + internal string ToRequestString (Uri uri) + { + if (_name.Length == 0) + return String.Empty; + + if (_version == 0) + return String.Format ("{0}={1}", _name, _value); + + var result = new StringBuilder (64); + result.AppendFormat ("$Version={0}; {1}={2}", _version, _name, _value); + + if (!_path.IsNullOrEmpty ()) + result.AppendFormat ("; $Path={0}", _path); + else if (uri != null) + result.AppendFormat ("; $Path={0}", uri.GetAbsolutePath ()); + else + result.Append ("; $Path=/"); + + bool appendDomain = uri == null || uri.Host != _domain; + if (appendDomain && !_domain.IsNullOrEmpty ()) + result.AppendFormat ("; $Domain={0}", _domain); + + if (!_port.IsNullOrEmpty ()) { + if (_port == "\"\"") + result.Append ("; $Port"); + else + result.AppendFormat ("; $Port={0}", _port); + } + + return result.ToString (); + } + + // From server to client + internal string ToResponseString () + { + return _name.Length > 0 + ? (_version == 0 ? toResponseStringVersion0 () : toResponseStringVersion1 ()) + : String.Empty; + } + + #endregion + + #region Public Methods + + /// + /// Determines whether the specified is equal to the current + /// . + /// + /// + /// An to compare with the current . + /// + /// + /// true if is equal to the current ; + /// otherwise, false. + /// + public override bool Equals (Object comparand) + { + var cookie = comparand as Cookie; + return cookie != null && + _name.Equals (cookie.Name, StringComparison.InvariantCultureIgnoreCase) && + _value.Equals (cookie.Value, StringComparison.InvariantCulture) && + _path.Equals (cookie.Path, StringComparison.InvariantCulture) && + _domain.Equals (cookie.Domain, StringComparison.InvariantCultureIgnoreCase) && + _version == cookie.Version; + } + + /// + /// Serves as a hash function for a object. + /// + /// + /// An that represents the hash code for the current . + /// + public override int GetHashCode () + { + return hash ( + StringComparer.InvariantCultureIgnoreCase.GetHashCode (_name), + _value.GetHashCode (), + _path.GetHashCode (), + StringComparer.InvariantCultureIgnoreCase.GetHashCode (_domain), + _version); + } + + /// + /// Returns a that represents the current . + /// + /// + /// This method returns a to use to send an HTTP Cookie to + /// an origin server. + /// + /// + /// A that represents the current . + /// + public override string ToString () + { + // i.e., only used for clients + // See para 4.2.2 of RFC 2109 and para 3.3.4 of RFC 2965 + // See also bug #316017 + return ToRequestString (null); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/CookieCollection.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/CookieCollection.cs new file mode 100644 index 0000000..389674e --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/CookieCollection.cs @@ -0,0 +1,598 @@ +#region License +/* + * CookieCollection.cs + * + * This code is derived from System.Net.CookieCollection.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2004,2009 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Lawrence Pit + * - Gonzalo Paniagua Javier + * - Sebastien Pouliot + */ +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace WebSocketSharp.Net +{ + /// + /// Provides a collection container for instances of the class. + /// + [Serializable] + public class CookieCollection : ICollection, IEnumerable + { + #region Private Fields + + private List _list; + private object _sync; + + #endregion + + #region Public Constructors + + /// + /// Initializes a new instance of the class. + /// + public CookieCollection () + { + _list = new List (); + } + + #endregion + + #region Internal Properties + + internal IList List { + get { + return _list; + } + } + + internal IEnumerable Sorted { + get { + var list = new List (_list); + if (list.Count > 1) + list.Sort (compareCookieWithinSorted); + + return list; + } + } + + #endregion + + #region Public Properties + + /// + /// Gets the number of cookies in the collection. + /// + /// + /// An that represents the number of cookies in the collection. + /// + public int Count { + get { + return _list.Count; + } + } + + /// + /// Gets a value indicating whether the collection is read-only. + /// + /// + /// true if the collection is read-only; otherwise, false. + /// The default value is true. + /// + public bool IsReadOnly { + // LAMESPEC: So how is one supposed to create a writable CookieCollection instance? + // We simply ignore this property, as this collection is always writable. + get { + return true; + } + } + + /// + /// Gets a value indicating whether the access to the collection is thread safe. + /// + /// + /// true if the access to the collection is thread safe; otherwise, false. + /// The default value is false. + /// + public bool IsSynchronized { + get { + return false; + } + } + + /// + /// Gets the at the specified from + /// the collection. + /// + /// + /// A at the specified in the collection. + /// + /// + /// An that represents the zero-based index of the + /// to find. + /// + /// + /// is out of allowable range of indexes for the collection. + /// + public Cookie this [int index] { + get { + if (index < 0 || index >= _list.Count) + throw new ArgumentOutOfRangeException ("index"); + + return _list [index]; + } + } + + /// + /// Gets the with the specified from + /// the collection. + /// + /// + /// A with the specified in the collection. + /// + /// + /// A that represents the name of the to find. + /// + /// + /// is . + /// + public Cookie this [string name] { + get { + if (name == null) + throw new ArgumentNullException ("name"); + + foreach (var cookie in Sorted) + if (cookie.Name.Equals (name, StringComparison.InvariantCultureIgnoreCase)) + return cookie; + + return null; + } + } + + /// + /// Gets an object used to synchronize access to the collection. + /// + /// + /// An used to synchronize access to the collection. + /// + public Object SyncRoot { + get { + return _sync ?? (_sync = ((ICollection) _list).SyncRoot); + } + } + + #endregion + + #region Private Methods + + private static int compareCookieWithinSort (Cookie x, Cookie y) + { + return (x.Name.Length + x.Value.Length) - (y.Name.Length + y.Value.Length); + } + + private static int compareCookieWithinSorted (Cookie x, Cookie y) + { + var ret = 0; + return (ret = x.Version - y.Version) != 0 + ? ret + : (ret = x.Name.CompareTo (y.Name)) != 0 + ? ret + : y.Path.Length - x.Path.Length; + } + + private static CookieCollection parseRequest (string value) + { + var cookies = new CookieCollection (); + + Cookie cookie = null; + var version = 0; + var pairs = splitCookieHeaderValue (value); + for (int i = 0; i < pairs.Length; i++) { + var pair = pairs [i].Trim (); + if (pair.Length == 0) + continue; + + if (pair.StartsWith ("$version", StringComparison.InvariantCultureIgnoreCase)) { + version = Int32.Parse (pair.GetValueInternal ("=").Trim ('"')); + } + else if (pair.StartsWith ("$path", StringComparison.InvariantCultureIgnoreCase)) { + if (cookie != null) + cookie.Path = pair.GetValueInternal ("="); + } + else if (pair.StartsWith ("$domain", StringComparison.InvariantCultureIgnoreCase)) { + if (cookie != null) + cookie.Domain = pair.GetValueInternal ("="); + } + else if (pair.StartsWith ("$port", StringComparison.InvariantCultureIgnoreCase)) { + var port = pair.Equals ("$port", StringComparison.InvariantCultureIgnoreCase) + ? "\"\"" + : pair.GetValueInternal ("="); + + if (cookie != null) + cookie.Port = port; + } + else { + if (cookie != null) + cookies.Add (cookie); + + string name; + string val = String.Empty; + + var pos = pair.IndexOf ('='); + if (pos == -1) { + name = pair; + } + else if (pos == pair.Length - 1) { + name = pair.Substring (0, pos).TrimEnd (' '); + } + else { + name = pair.Substring (0, pos).TrimEnd (' '); + val = pair.Substring (pos + 1).TrimStart (' '); + } + + cookie = new Cookie (name, val); + if (version != 0) + cookie.Version = version; + } + } + + if (cookie != null) + cookies.Add (cookie); + + return cookies; + } + + private static CookieCollection parseResponse (string value) + { + var cookies = new CookieCollection (); + + Cookie cookie = null; + var pairs = splitCookieHeaderValue (value); + for (int i = 0; i < pairs.Length; i++) { + var pair = pairs [i].Trim (); + if (pair.Length == 0) + continue; + + if (pair.StartsWith ("version", StringComparison.InvariantCultureIgnoreCase)) { + if (cookie != null) + cookie.Version = Int32.Parse (pair.GetValueInternal ("=").Trim ('"')); + } + else if (pair.StartsWith ("expires", StringComparison.InvariantCultureIgnoreCase)) { + var buffer = new StringBuilder (pair.GetValueInternal ("="), 32); + if (i < pairs.Length - 1) + buffer.AppendFormat (", {0}", pairs [++i].Trim ()); + + DateTime expires; + if (!DateTime.TryParseExact ( + buffer.ToString (), + new [] { "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'", "r" }, + CultureInfo.CreateSpecificCulture ("en-US"), + DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, + out expires)) + expires = DateTime.Now; + + if (cookie != null && cookie.Expires == DateTime.MinValue) + cookie.Expires = expires.ToLocalTime (); + } + else if (pair.StartsWith ("max-age", StringComparison.InvariantCultureIgnoreCase)) { + var max = Int32.Parse (pair.GetValueInternal ("=").Trim ('"')); + var expires = DateTime.Now.AddSeconds ((double) max); + if (cookie != null) + cookie.Expires = expires; + } + else if (pair.StartsWith ("path", StringComparison.InvariantCultureIgnoreCase)) { + if (cookie != null) + cookie.Path = pair.GetValueInternal ("="); + } + else if (pair.StartsWith ("domain", StringComparison.InvariantCultureIgnoreCase)) { + if (cookie != null) + cookie.Domain = pair.GetValueInternal ("="); + } + else if (pair.StartsWith ("port", StringComparison.InvariantCultureIgnoreCase)) { + var port = pair.Equals ("port", StringComparison.InvariantCultureIgnoreCase) + ? "\"\"" + : pair.GetValueInternal ("="); + + if (cookie != null) + cookie.Port = port; + } + else if (pair.StartsWith ("comment", StringComparison.InvariantCultureIgnoreCase)) { + if (cookie != null) + cookie.Comment = pair.GetValueInternal ("=").UrlDecode (); + } + else if (pair.StartsWith ("commenturl", StringComparison.InvariantCultureIgnoreCase)) { + if (cookie != null) + cookie.CommentUri = pair.GetValueInternal ("=").Trim ('"').ToUri (); + } + else if (pair.StartsWith ("discard", StringComparison.InvariantCultureIgnoreCase)) { + if (cookie != null) + cookie.Discard = true; + } + else if (pair.StartsWith ("secure", StringComparison.InvariantCultureIgnoreCase)) { + if (cookie != null) + cookie.Secure = true; + } + else if (pair.StartsWith ("httponly", StringComparison.InvariantCultureIgnoreCase)) { + if (cookie != null) + cookie.HttpOnly = true; + } + else { + if (cookie != null) + cookies.Add (cookie); + + string name; + string val = String.Empty; + + var pos = pair.IndexOf ('='); + if (pos == -1) { + name = pair; + } + else if (pos == pair.Length - 1) { + name = pair.Substring (0, pos).TrimEnd (' '); + } + else { + name = pair.Substring (0, pos).TrimEnd (' '); + val = pair.Substring (pos + 1).TrimStart (' '); + } + + cookie = new Cookie (name, val); + } + } + + if (cookie != null) + cookies.Add (cookie); + + return cookies; + } + + private int searchCookie (Cookie cookie) + { + var name = cookie.Name; + var path = cookie.Path; + var domain = cookie.Domain; + var version = cookie.Version; + + for (int i = _list.Count - 1; i >= 0; i--) { + var c = _list [i]; + if (c.Name.Equals (name, StringComparison.InvariantCultureIgnoreCase) && + c.Path.Equals (path, StringComparison.InvariantCulture) && + c.Domain.Equals (domain, StringComparison.InvariantCultureIgnoreCase) && + c.Version == version) + return i; + } + + return -1; + } + + private static string [] splitCookieHeaderValue (string value) + { + return new List (value.SplitHeaderValue (',', ';')).ToArray (); + } + + #endregion + + #region Internal Methods + + internal static CookieCollection Parse (string value, bool response) + { + return response + ? parseResponse (value) + : parseRequest (value); + } + + internal void SetOrRemove (Cookie cookie) + { + var pos = searchCookie (cookie); + if (pos == -1) { + if (!cookie.Expired) + _list.Add (cookie); + + return; + } + + if (!cookie.Expired) { + _list [pos] = cookie; + return; + } + + _list.RemoveAt (pos); + } + + internal void SetOrRemove (CookieCollection cookies) + { + foreach (Cookie cookie in cookies) + SetOrRemove (cookie); + } + + internal void Sort () + { + if (_list.Count > 1) + _list.Sort (compareCookieWithinSort); + } + + #endregion + + #region Public Methods + + /// + /// Adds the specified to the collection. + /// + /// + /// A to add. + /// + /// + /// is . + /// + public void Add (Cookie cookie) + { + if (cookie == null) + throw new ArgumentNullException ("cookie"); + + var pos = searchCookie (cookie); + if (pos == -1) { + _list.Add (cookie); + return; + } + + _list [pos] = cookie; + } + + /// + /// Adds the specified to the collection. + /// + /// + /// A that contains the cookies to add. + /// + /// + /// is . + /// + public void Add (CookieCollection cookies) + { + if (cookies == null) + throw new ArgumentNullException ("cookies"); + + foreach (Cookie cookie in cookies) + Add (cookie); + } + + /// + /// Copies the elements of the collection to the specified , starting at + /// the specified in the . + /// + /// + /// An that represents the destination of the elements copied from + /// the collection. + /// + /// + /// An that represents the zero-based index in + /// at which copying begins. + /// + /// + /// is . + /// + /// + /// is less than zero. + /// + /// + /// + /// is multidimensional. + /// + /// + /// -or- + /// + /// + /// The number of elements in the collection is greater than the available space from + /// to the end of the destination . + /// + /// + /// + /// The elements in the collection cannot be cast automatically to the type of the destination + /// . + /// + public void CopyTo (Array array, int index) + { + if (array == null) + throw new ArgumentNullException ("array"); + + if (index < 0) + throw new ArgumentOutOfRangeException ("index", "Less than zero."); + + if (array.Rank > 1) + throw new ArgumentException ("Multidimensional.", "array"); + + if (array.Length - index < _list.Count) + throw new ArgumentException ( + "The number of elements in this collection is greater than the available space of the destination array."); + + if (!array.GetType ().GetElementType ().IsAssignableFrom (typeof (Cookie))) + throw new InvalidCastException ( + "The elements in this collection cannot be cast automatically to the type of the destination array."); + + ((IList) _list).CopyTo (array, index); + } + + /// + /// Copies the elements of the collection to the specified array of , + /// starting at the specified in the . + /// + /// + /// An array of that represents the destination of the elements + /// copied from the collection. + /// + /// + /// An that represents the zero-based index in + /// at which copying begins. + /// + /// + /// is . + /// + /// + /// is less than zero. + /// + /// + /// The number of elements in the collection is greater than the available space from + /// to the end of the destination . + /// + public void CopyTo (Cookie [] array, int index) + { + if (array == null) + throw new ArgumentNullException ("array"); + + if (index < 0) + throw new ArgumentOutOfRangeException ("index", "Less than zero."); + + if (array.Length - index < _list.Count) + throw new ArgumentException ( + "The number of elements in this collection is greater than the available space of the destination array."); + + _list.CopyTo (array, index); + } + + /// + /// Gets the enumerator used to iterate through the collection. + /// + /// + /// An instance used to iterate through the collection. + /// + public IEnumerator GetEnumerator () + { + return _list.GetEnumerator (); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/CookieException.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/CookieException.cs new file mode 100644 index 0000000..d96e7f7 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/CookieException.cs @@ -0,0 +1,149 @@ +#region License +/* + * CookieException.cs + * + * This code is derived from System.Net.CookieException.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Lawrence Pit + */ +#endregion + +using System; +using System.Runtime.Serialization; +using System.Security.Permissions; + +namespace WebSocketSharp.Net +{ + /// + /// The exception that is thrown when a gets an error. + /// + [Serializable] + public class CookieException : FormatException, ISerializable + { + #region Internal Constructors + + internal CookieException (string message) + : base (message) + { + } + + internal CookieException (string message, Exception innerException) + : base (message, innerException) + { + } + + #endregion + + #region Protected Constructors + + /// + /// Initializes a new instance of the class from + /// the specified and . + /// + /// + /// A that contains the serialized object data. + /// + /// + /// A that specifies the source for the deserialization. + /// + protected CookieException ( + SerializationInfo serializationInfo, StreamingContext streamingContext) + : base (serializationInfo, streamingContext) + { + } + + #endregion + + #region Public Constructors + + /// + /// Initializes a new instance of the class. + /// + public CookieException () + : base () + { + } + + #endregion + + #region Public Methods + + /// + /// Populates the specified with the data needed to serialize + /// the current . + /// + /// + /// A that holds the serialized object data. + /// + /// + /// A that specifies the destination for the serialization. + /// + /** + * FIXME: Removed to avoid Unity warnings + * [SecurityPermission ( + * SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] + */ + public override void GetObjectData ( + SerializationInfo serializationInfo, StreamingContext streamingContext) + { + base.GetObjectData (serializationInfo, streamingContext); + } + + #endregion + + #region Explicit Interface Implementation + + /// + /// Populates the specified with the data needed to serialize + /// the current . + /// + /// + /// A that holds the serialized object data. + /// + /// + /// A that specifies the destination for the serialization. + /// + /** + * FIXME: Removed to avoid Unity warnings + * [SecurityPermission ( + * SecurityAction.LinkDemand, + * Flags = SecurityPermissionFlag.SerializationFormatter, + * SerializationFormatter = true)] + */ + void ISerializable.GetObjectData ( + SerializationInfo serializationInfo, StreamingContext streamingContext) + { + base.GetObjectData (serializationInfo, streamingContext); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/EndPointListener.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/EndPointListener.cs new file mode 100644 index 0000000..3a8ec42 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/EndPointListener.cs @@ -0,0 +1,478 @@ +#region License +/* + * EndPointListener.cs + * + * This code is derived from System.Net.EndPointListener.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Threading; + +namespace WebSocketSharp.Net +{ + internal sealed class EndPointListener + { + #region Private Static Fields + + private static readonly string _defaultCertFolderPath = + Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData); + + #endregion + + #region Private Fields + + private List _all; // host = '+' + private X509Certificate2 _cert; + private IPEndPoint _endpoint; + private Dictionary _prefixes; + private bool _secure; + private Socket _socket; + private List _unhandled; // host = '*' + private Dictionary _unregistered; + + #endregion + + #region Public Constructors + + public EndPointListener ( + IPAddress address, int port, bool secure, string certFolderPath, X509Certificate2 defaultCert) + { + if (secure) { + _secure = secure; + _cert = getCertificate (port, certFolderPath, defaultCert); + if (_cert == null) + throw new ArgumentException ("No server certificate found."); + } + + _endpoint = new IPEndPoint (address, port); + _prefixes = new Dictionary (); + _unregistered = new Dictionary (); + + _socket = new Socket (address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + _socket.Bind (_endpoint); + _socket.Listen (500); + + var args = new SocketAsyncEventArgs (); + args.UserToken = this; + args.Completed += onAccept; + _socket.AcceptAsync (args); + } + + #endregion + + #region Public Properties + + public X509Certificate2 Certificate { + get { + return _cert; + } + } + + public bool IsSecure { + get { + return _secure; + } + } + + #endregion + + #region Private Methods + + private static void addSpecial (List prefixes, ListenerPrefix prefix) + { + if (prefixes == null) + return; + + var path = prefix.Path; + foreach (var pref in prefixes) + if (pref.Path == path) // TODO: Code? + throw new HttpListenerException (400, "Prefix already in use."); + + prefixes.Add (prefix); + } + + private void checkIfRemove () + { + if (_prefixes.Count > 0) + return; + + if (_unhandled != null && _unhandled.Count > 0) + return; + + if (_all != null && _all.Count > 0) + return; + + EndPointManager.RemoveEndPoint (this, _endpoint); + } + + private static RSACryptoServiceProvider createRSAFromFile (string filename) + { + byte [] pvk = null; + using (var fs = File.Open (filename, FileMode.Open, FileAccess.Read, FileShare.Read)) { + pvk = new byte [fs.Length]; + fs.Read (pvk, 0, pvk.Length); + } + + var rsa = new RSACryptoServiceProvider (); + rsa.ImportCspBlob (pvk); + + return rsa; + } + + private static X509Certificate2 getCertificate ( + int port, string certFolderPath, X509Certificate2 defaultCert) + { + if (certFolderPath == null || certFolderPath.Length == 0) + certFolderPath = _defaultCertFolderPath; + + try { + var cer = Path.Combine (certFolderPath, String.Format ("{0}.cer", port)); + var key = Path.Combine (certFolderPath, String.Format ("{0}.key", port)); + if (File.Exists (cer) && File.Exists (key)) { + var cert = new X509Certificate2 (cer); + cert.PrivateKey = createRSAFromFile (key); + + return cert; + } + } + catch { + } + + return defaultCert; + } + + private static HttpListener matchFromList ( + string host, string path, List list, out ListenerPrefix prefix) + { + prefix = null; + if (list == null) + return null; + + HttpListener bestMatch = null; + var bestLength = -1; + foreach (var pref in list) { + var ppath = pref.Path; + if (ppath.Length < bestLength) + continue; + + if (path.StartsWith (ppath)) { + bestLength = ppath.Length; + bestMatch = pref.Listener; + prefix = pref; + } + } + + return bestMatch; + } + + private static void onAccept (object sender, EventArgs e) + { + var args = (SocketAsyncEventArgs) e; + var listener = (EndPointListener) args.UserToken; + Socket accepted = null; + if (args.SocketError == SocketError.Success) { + accepted = args.AcceptSocket; + args.AcceptSocket = null; + } + + try { + listener._socket.AcceptAsync (args); + } + catch { + if (accepted != null) + accepted.Close (); + + return; + } + + if (accepted == null) + return; + + HttpConnection conn = null; + try { + conn = new HttpConnection (accepted, listener); + lock (((ICollection) listener._unregistered).SyncRoot) + listener._unregistered [conn] = conn; + + conn.BeginReadRequest (); + } + catch { + if (conn != null) { + conn.Close (true); + return; + } + + accepted.Close (); + } + } + + private static bool removeSpecial (List prefixes, ListenerPrefix prefix) + { + if (prefixes == null) + return false; + + var path = prefix.Path; + var count = prefixes.Count; + for (int i = 0; i < count; i++) { + if (prefixes [i].Path == path) { + prefixes.RemoveAt (i); + return true; + } + } + + return false; + } + + private HttpListener searchListener (Uri uri, out ListenerPrefix prefix) + { + prefix = null; + if (uri == null) + return null; + + var host = uri.Host; + var port = uri.Port; + var path = HttpUtility.UrlDecode (uri.AbsolutePath); + var pathSlash = path [path.Length - 1] == '/' ? path : path + "/"; + + HttpListener bestMatch = null; + var bestLength = -1; + if (host != null && host.Length > 0) { + foreach (var pref in _prefixes.Keys) { + var ppath = pref.Path; + if (ppath.Length < bestLength) + continue; + + if (pref.Host != host || pref.Port != port) + continue; + + if (path.StartsWith (ppath) || pathSlash.StartsWith (ppath)) { + bestLength = ppath.Length; + bestMatch = _prefixes [pref]; + prefix = pref; + } + } + + if (bestLength != -1) + return bestMatch; + } + + var list = _unhandled; + bestMatch = matchFromList (host, path, list, out prefix); + if (path != pathSlash && bestMatch == null) + bestMatch = matchFromList (host, pathSlash, list, out prefix); + + if (bestMatch != null) + return bestMatch; + + list = _all; + bestMatch = matchFromList (host, path, list, out prefix); + if (path != pathSlash && bestMatch == null) + bestMatch = matchFromList (host, pathSlash, list, out prefix); + + if (bestMatch != null) + return bestMatch; + + return null; + } + + #endregion + + #region Internal Methods + + internal static bool CertificateExists (int port, string certFolderPath) + { + if (certFolderPath == null || certFolderPath.Length == 0) + certFolderPath = _defaultCertFolderPath; + + var cer = Path.Combine (certFolderPath, String.Format ("{0}.cer", port)); + var key = Path.Combine (certFolderPath, String.Format ("{0}.key", port)); + + return File.Exists (cer) && File.Exists (key); + } + + internal void RemoveConnection (HttpConnection connection) + { + lock (((ICollection) _unregistered).SyncRoot) + _unregistered.Remove (connection); + } + + #endregion + + #region Public Methods + + public void AddPrefix (ListenerPrefix prefix, HttpListener listener) + { + List current, future; + if (prefix.Host == "*") { + do { + current = _unhandled; + future = current != null + ? new List (current) + : new List (); + + prefix.Listener = listener; + addSpecial (future, prefix); + } + while (Interlocked.CompareExchange (ref _unhandled, future, current) != current); + + return; + } + + if (prefix.Host == "+") { + do { + current = _all; + future = current != null + ? new List (current) + : new List (); + + prefix.Listener = listener; + addSpecial (future, prefix); + } + while (Interlocked.CompareExchange (ref _all, future, current) != current); + + return; + } + + Dictionary prefs, prefs2; + do { + prefs = _prefixes; + if (prefs.ContainsKey (prefix)) { + var other = prefs [prefix]; + if (other != listener) // TODO: Code? + throw new HttpListenerException (400, "There's another listener for " + prefix); + + return; + } + + prefs2 = new Dictionary (prefs); + prefs2 [prefix] = listener; + } + while (Interlocked.CompareExchange (ref _prefixes, prefs2, prefs) != prefs); + } + + public bool BindContext (HttpListenerContext context) + { + ListenerPrefix prefix; + var listener = searchListener (context.Request.Url, out prefix); + if (listener == null) + return false; + + context.Listener = listener; + context.Connection.Prefix = prefix; + + return true; + } + + public void Close () + { + _socket.Close (); + + lock (((ICollection) _unregistered).SyncRoot) { + var conns = new List (_unregistered.Keys); + _unregistered.Clear (); + foreach (var conn in conns) + conn.Close (true); + + conns.Clear (); + } + } + + public void RemovePrefix (ListenerPrefix prefix, HttpListener listener) + { + List current, future; + if (prefix.Host == "*") { + do { + current = _unhandled; + future = current != null + ? new List (current) + : new List (); + + if (!removeSpecial (future, prefix)) + break; // Prefix not found. + } + while (Interlocked.CompareExchange (ref _unhandled, future, current) != current); + + checkIfRemove (); + return; + } + + if (prefix.Host == "+") { + do { + current = _all; + future = current != null + ? new List (current) + : new List (); + + if (!removeSpecial (future, prefix)) + break; // Prefix not found. + } + while (Interlocked.CompareExchange (ref _all, future, current) != current); + + checkIfRemove (); + return; + } + + Dictionary prefs, prefs2; + do { + prefs = _prefixes; + if (!prefs.ContainsKey (prefix)) + break; + + prefs2 = new Dictionary (prefs); + prefs2.Remove (prefix); + } + while (Interlocked.CompareExchange (ref _prefixes, prefs2, prefs) != prefs); + + checkIfRemove (); + } + + public void UnbindContext (HttpListenerContext context) + { + if (context == null || context.Listener == null) + return; + + context.Listener.UnregisterContext (context); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/EndPointManager.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/EndPointManager.cs new file mode 100644 index 0000000..3429ce3 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/EndPointManager.cs @@ -0,0 +1,183 @@ +#region License +/* + * EndPointManager.cs + * + * This code is derived from System.Net.EndPointManager.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Net; + +namespace WebSocketSharp.Net +{ + internal sealed class EndPointManager + { + #region Private Static Fields + + private static Dictionary> _ipToEndpoints = + new Dictionary> (); + + #endregion + + #region Private Constructors + + private EndPointManager () + { + } + + #endregion + + #region Private Methods + + private static void addPrefix (string uriPrefix, HttpListener httpListener) + { + var prefix = new ListenerPrefix (uriPrefix); + if (prefix.Path.IndexOf ('%') != -1) + throw new HttpListenerException (400, "Invalid path."); // TODO: Code? + + if (prefix.Path.IndexOf ("//", StringComparison.Ordinal) != -1) + throw new HttpListenerException (400, "Invalid path."); // TODO: Code? + + // Always listens on all the interfaces, no matter the host name/ip used. + var epListener = getEndPointListener ( + IPAddress.Any, prefix.Port, httpListener, prefix.Secure); + + epListener.AddPrefix (prefix, httpListener); + } + + private static EndPointListener getEndPointListener ( + IPAddress address, int port, HttpListener httpListener, bool secure) + { + Dictionary endpoints = null; + if (_ipToEndpoints.ContainsKey (address)) { + endpoints = _ipToEndpoints [address]; + } + else { + endpoints = new Dictionary (); + _ipToEndpoints [address] = endpoints; + } + + EndPointListener epListener = null; + if (endpoints.ContainsKey (port)) { + epListener = endpoints [port]; + } + else { + epListener = new EndPointListener ( + address, + port, + secure, + httpListener.CertificateFolderPath, + httpListener.DefaultCertificate); + + endpoints [port] = epListener; + } + + return epListener; + } + + private static void removePrefix (string uriPrefix, HttpListener httpListener) + { + var prefix = new ListenerPrefix (uriPrefix); + if (prefix.Path.IndexOf ('%') != -1) + return; + + if (prefix.Path.IndexOf ("//", StringComparison.Ordinal) != -1) + return; + + var epListener = getEndPointListener ( + IPAddress.Any, prefix.Port, httpListener, prefix.Secure); + + epListener.RemovePrefix (prefix, httpListener); + } + + #endregion + + #region Public Methods + + public static void AddListener (HttpListener httpListener) + { + var added = new List (); + lock (((ICollection) _ipToEndpoints).SyncRoot) { + try { + foreach (var prefix in httpListener.Prefixes) { + addPrefix (prefix, httpListener); + added.Add (prefix); + } + } + catch { + foreach (var prefix in added) + removePrefix (prefix, httpListener); + + throw; + } + } + } + + public static void AddPrefix (string uriPrefix, HttpListener httpListener) + { + lock (((ICollection) _ipToEndpoints).SyncRoot) + addPrefix (uriPrefix, httpListener); + } + + public static void RemoveEndPoint (EndPointListener epListener, IPEndPoint endpoint) + { + lock (((ICollection) _ipToEndpoints).SyncRoot) { + var endpoints = _ipToEndpoints [endpoint.Address]; + endpoints.Remove (endpoint.Port); + if (endpoints.Count == 0) + _ipToEndpoints.Remove (endpoint.Address); + + epListener.Close (); + } + } + + public static void RemoveListener (HttpListener httpListener) + { + lock (((ICollection) _ipToEndpoints).SyncRoot) + foreach (var prefix in httpListener.Prefixes) + removePrefix (prefix, httpListener); + } + + public static void RemovePrefix (string uriPrefix, HttpListener httpListener) + { + lock (((ICollection) _ipToEndpoints).SyncRoot) + removePrefix (uriPrefix, httpListener); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpBasicIdentity.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpBasicIdentity.cs new file mode 100644 index 0000000..d55cd9f --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpBasicIdentity.cs @@ -0,0 +1,82 @@ +#region License +/* + * HttpBasicIdentity.cs + * + * This code is derived from System.Net.HttpListenerBasicIdentity.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; +using System.Security.Principal; + +namespace WebSocketSharp.Net +{ + /// + /// Holds the user name and password from the HTTP Basic authentication credentials. + /// + public class HttpBasicIdentity : GenericIdentity + { + #region Private Fields + + private string _password; + + #endregion + + #region internal Constructors + + internal HttpBasicIdentity (string username, string password) + : base (username, "Basic") + { + _password = password; + } + + #endregion + + #region Public Properties + + /// + /// Gets the password from the HTTP Basic authentication credentials. + /// + /// + /// A that represents the password. + /// + public virtual string Password { + get { + return _password; + } + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpConnection.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpConnection.cs new file mode 100644 index 0000000..cd4b1f0 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpConnection.cs @@ -0,0 +1,564 @@ +#region License +/* + * HttpConnection.cs + * + * This code is derived from System.Net.HttpConnection.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using WebSocketSharp.Net.Security; + +namespace WebSocketSharp.Net +{ + internal sealed class HttpConnection + { + #region Private Const Fields + + private const int _bufferSize = 8192; + + #endregion + + #region Private Fields + + private byte [] _buffer; + private bool _chunked; + private HttpListenerContext _context; + private bool _contextWasBound; + private StringBuilder _currentLine; + private InputState _inputState; + private RequestStream _inputStream; + private HttpListener _lastListener; + private LineState _lineState; + private EndPointListener _listener; + private ResponseStream _outputStream; + private int _position; + private ListenerPrefix _prefix; + private MemoryStream _requestBuffer; + private int _reuses; + private bool _secure; + private Socket _socket; + private Stream _stream; + private object _sync; + private int _timeout; + private Timer _timer; + private WebSocketStream _websocketStream; + + #endregion + + #region Public Constructors + + public HttpConnection (Socket socket, EndPointListener listener) + { + _socket = socket; + _listener = listener; + _secure = listener.IsSecure; + + var netStream = new NetworkStream (socket, false); + if (_secure) { + var sslStream = new SslStream (netStream, false); + sslStream.AuthenticateAsServer (listener.Certificate); + _stream = sslStream; + } + else { + _stream = netStream; + } + + _sync = new object (); + _timeout = 90000; // 90k ms for first request, 15k ms from then on. + _timer = new Timer (onTimeout, this, Timeout.Infinite, Timeout.Infinite); + + init (); + } + + #endregion + + #region Public Properties + + public bool IsClosed { + get { + return _socket == null; + } + } + + public bool IsSecure { + get { + return _secure; + } + } + + public IPEndPoint LocalEndPoint { + get { + return (IPEndPoint) _socket.LocalEndPoint; + } + } + + public ListenerPrefix Prefix { + get { + return _prefix; + } + + set { + _prefix = value; + } + } + + public IPEndPoint RemoteEndPoint { + get { + return (IPEndPoint) _socket.RemoteEndPoint; + } + } + + public int Reuses { + get { + return _reuses; + } + } + + public Stream Stream { + get { + return _stream; + } + } + + #endregion + + #region Private Methods + + private void close () + { + lock (_sync) { + if (_socket == null) + return; + + disposeTimer (); + disposeRequestBuffer (); + disposeStream (); + closeSocket (); + } + + unbind (); + removeConnection (); + } + + private void closeSocket () + { + try { + _socket.Shutdown (SocketShutdown.Both); + } + catch { + } + + _socket.Close (); + _socket = null; + } + + private void disposeRequestBuffer () + { + if (_requestBuffer == null) + return; + + _requestBuffer.Dispose (); + _requestBuffer = null; + } + + private void disposeStream () + { + if (_stream == null) + return; + + _inputStream = null; + _outputStream = null; + _websocketStream = null; + + _stream.Dispose (); + _stream = null; + } + + private void disposeTimer () + { + if (_timer == null) + return; + + try { + _timer.Change (Timeout.Infinite, Timeout.Infinite); + } + catch { + } + + _timer.Dispose (); + _timer = null; + } + + private void init () + { + _chunked = false; + _context = new HttpListenerContext (this); + _inputState = InputState.RequestLine; + _inputStream = null; + _lineState = LineState.None; + _outputStream = null; + _position = 0; + _prefix = null; + _requestBuffer = new MemoryStream (); + } + + private static void onRead (IAsyncResult asyncResult) + { + var conn = (HttpConnection) asyncResult.AsyncState; + if (conn._socket == null) + return; + + lock (conn._sync) { + if (conn._socket == null) + return; + + var nread = -1; + try { + conn._timer.Change (Timeout.Infinite, Timeout.Infinite); + nread = conn._stream.EndRead (asyncResult); + conn._requestBuffer.Write (conn._buffer, 0, nread); + if (conn._requestBuffer.Length > 32768) { + conn.SendError ("Bad request", 400); + conn.Close (true); + + return; + } + } + catch { + if (conn._requestBuffer != null && conn._requestBuffer.Length > 0) + conn.SendError (); + + conn.close (); + return; + } + + if (nread <= 0) { + conn.close (); + return; + } + + if (conn.processInput (conn._requestBuffer.GetBuffer ())) { + if (!conn._context.HasError) + conn._context.Request.FinishInitialization (); + + if (conn._context.HasError) { + conn.SendError (); + conn.Close (true); + + return; + } + + if (!conn._listener.BindContext (conn._context)) { + conn.SendError ("Invalid host", 400); + conn.Close (true); + + return; + } + + var listener = conn._context.Listener; + if (conn._lastListener != listener) { + conn.removeConnection (); + listener.AddConnection (conn); + conn._lastListener = listener; + } + + conn._contextWasBound = true; + listener.RegisterContext (conn._context); + + return; + } + + conn._stream.BeginRead (conn._buffer, 0, _bufferSize, onRead, conn); + } + } + + private static void onTimeout (object state) + { + var conn = (HttpConnection) state; + conn.close (); + } + + // true -> Done processing. + // false -> Need more input. + private bool processInput (byte [] data) + { + var len = data.Length; + var used = 0; + string line; + try { + while ((line = readLine (data, _position, len - _position, ref used)) != null) { + _position += used; + if (line.Length == 0) { + if (_inputState == InputState.RequestLine) + continue; + + _currentLine = null; + return true; + } + + if (_inputState == InputState.RequestLine) { + _context.Request.SetRequestLine (line); + _inputState = InputState.Headers; + } + else { + _context.Request.AddHeader (line); + } + + if (_context.HasError) + return true; + } + } + catch (Exception ex) { + _context.ErrorMessage = ex.Message; + return true; + } + + _position += used; + if (used == len) { + _requestBuffer.SetLength (0); + _position = 0; + } + + return false; + } + + private string readLine (byte [] buffer, int offset, int length, ref int used) + { + if (_currentLine == null) + _currentLine = new StringBuilder (); + + var last = offset + length; + used = 0; + for (int i = offset; i < last && _lineState != LineState.LF; i++) { + used++; + var b = buffer [i]; + if (b == 13) + _lineState = LineState.CR; + else if (b == 10) + _lineState = LineState.LF; + else + _currentLine.Append ((char) b); + } + + string res = null; + if (_lineState == LineState.LF) { + _lineState = LineState.None; + res = _currentLine.ToString (); + _currentLine.Length = 0; + } + + return res; + } + + private void removeConnection () + { + if (_lastListener == null) + _listener.RemoveConnection (this); + else + _lastListener.RemoveConnection (this); + } + + private void unbind () + { + if (_contextWasBound) { + _listener.UnbindContext (_context); + _contextWasBound = false; + } + } + + #endregion + + #region Internal Methods + + internal void Close (bool force) + { + if (_socket == null) + return; + + lock (_sync) { + if (_socket == null) + return; + + if (!force) { + GetResponseStream ().Close (); + + var req = _context.Request; + var res = _context.Response; + if (req.KeepAlive && + !res.CloseConnection && + req.FlushInput () && + (!_chunked || (_chunked && !res.ForceCloseChunked))) { + // Don't close. Keep working. + _reuses++; + disposeRequestBuffer (); + unbind (); + init (); + BeginReadRequest (); + + return; + } + } + + close (); + } + } + + #endregion + + #region Public Methods + + public void BeginReadRequest () + { + if (_buffer == null) + _buffer = new byte [_bufferSize]; + + if (_reuses == 1) + _timeout = 15000; + + try { + _timer.Change (_timeout, Timeout.Infinite); + _stream.BeginRead (_buffer, 0, _bufferSize, onRead, this); + } + catch { + close (); + } + } + + public void Close () + { + Close (false); + } + + public RequestStream GetRequestStream (bool chunked, long contentlength) + { + if (_inputStream != null || _socket == null) + return _inputStream; + + lock (_sync) { + if (_socket == null) + return _inputStream; + + var buff = _requestBuffer.GetBuffer (); + var len = buff.Length; + disposeRequestBuffer (); + if (chunked) { + _chunked = true; + _context.Response.SendChunked = true; + _inputStream = new ChunkedRequestStream ( + _context, _stream, buff, _position, len - _position); + } + else { + _inputStream = new RequestStream ( + _stream, buff, _position, len - _position, contentlength); + } + + return _inputStream; + } + } + + public ResponseStream GetResponseStream () + { + // TODO: Can we get this stream before reading the input? + + if (_outputStream != null || _socket == null) + return _outputStream; + + lock (_sync) { + if (_socket == null) + return _outputStream; + + var listener = _context.Listener; + var ignore = listener == null ? true : listener.IgnoreWriteExceptions; + _outputStream = new ResponseStream (_stream, _context.Response, ignore); + + return _outputStream; + } + } + + public WebSocketStream GetWebSocketStream () + { + if (_websocketStream != null || _socket == null) + return _websocketStream; + + lock (_sync) { + if (_socket == null) + return _websocketStream; + + _websocketStream = new WebSocketStream (_stream, _secure); + return _websocketStream; + } + } + + public void SendError () + { + SendError (_context.ErrorMessage, _context.ErrorStatus); + } + + public void SendError (string message, int status) + { + if (_socket == null) + return; + + lock (_sync) { + if (_socket == null) + return; + + try { + var res = _context.Response; + res.StatusCode = status; + res.ContentType = "text/html"; + + var desc = status.GetStatusDescription (); + var msg = message != null && message.Length > 0 + ? String.Format ("

{0} ({1})

", desc, message) + : String.Format ("

{0}

", desc); + + var entity = res.ContentEncoding.GetBytes (msg); + res.Close (entity, false); + } + catch { + // Response was already closed. + } + } + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpDigestIdentity.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpDigestIdentity.cs new file mode 100644 index 0000000..caa471a --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpDigestIdentity.cs @@ -0,0 +1,183 @@ +#region License +/* + * HttpDigestIdentity.cs + * + * The MIT License + * + * Copyright (c) 2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Collections.Specialized; +using System.Security.Principal; + +namespace WebSocketSharp.Net +{ + /// + /// Holds the user name and other parameters from the HTTP Digest authentication credentials. + /// + public class HttpDigestIdentity : GenericIdentity + { + #region Private Fields + + private NameValueCollection _parameters; + + #endregion + + #region Internal Constructors + + internal HttpDigestIdentity (NameValueCollection parameters) + : base (parameters ["username"], "Digest") + { + _parameters = parameters; + } + + #endregion + + #region Public Properties + + /// + /// Gets the algorithm parameter from the HTTP Digest authentication credentials. + /// + /// + /// A that represents the algorithm parameter. + /// + public string Algorithm { + get { + return _parameters ["algorithm"]; + } + } + + /// + /// Gets the cnonce parameter from the HTTP Digest authentication credentials. + /// + /// + /// A that represents the cnonce parameter. + /// + public string Cnonce { + get { + return _parameters ["cnonce"]; + } + } + + /// + /// Gets the nc parameter from the HTTP Digest authentication credentials. + /// + /// + /// A that represents the nc parameter. + /// + public string Nc { + get { + return _parameters ["nc"]; + } + } + + /// + /// Gets the nonce parameter from the HTTP Digest authentication credentials. + /// + /// + /// A that represents the nonce parameter. + /// + public string Nonce { + get { + return _parameters ["nonce"]; + } + } + + /// + /// Gets the opaque parameter from the HTTP Digest authentication credentials. + /// + /// + /// A that represents the opaque parameter. + /// + public string Opaque { + get { + return _parameters ["opaque"]; + } + } + + /// + /// Gets the qop parameter from the HTTP Digest authentication credentials. + /// + /// + /// A that represents the qop parameter. + /// + public string Qop { + get { + return _parameters ["qop"]; + } + } + + /// + /// Gets the realm parameter from the HTTP Digest authentication credentials. + /// + /// + /// A that represents the realm parameter. + /// + public string Realm { + get { + return _parameters ["realm"]; + } + } + + /// + /// Gets the response parameter from the HTTP Digest authentication credentials. + /// + /// + /// A that represents the response parameter. + /// + public string Response { + get { + return _parameters ["response"]; + } + } + + /// + /// Gets the uri parameter from the HTTP Digest authentication credentials. + /// + /// + /// A that represents the uri parameter. + /// + public string Uri { + get { + return _parameters ["uri"]; + } + } + + #endregion + + #region Internal Methods + + internal bool IsValid (string password, string realm, string method, string entity) + { + var parameters = new NameValueCollection (_parameters); + parameters ["password"] = password; + parameters ["realm"] = realm; + parameters ["method"] = method; + parameters ["entity"] = entity; + + return _parameters ["response"] == AuthenticationResponse.CreateRequestDigest (parameters); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpHeaderInfo.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpHeaderInfo.cs new file mode 100644 index 0000000..f009c2d --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpHeaderInfo.cs @@ -0,0 +1,119 @@ +#region License +/* + * HttpHeaderInfo.cs + * + * The MIT License + * + * Copyright (c) 2013-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; + +namespace WebSocketSharp.Net +{ + internal class HttpHeaderInfo + { + #region Private Fields + + private HttpHeaderType _type; + + #endregion + + #region Public Constructors + + public HttpHeaderInfo () + { + } + + #endregion + + #region Internal Properties + + internal bool IsMultiValueInRequest { + get { + return (_type & HttpHeaderType.MultiValueInRequest) == HttpHeaderType.MultiValueInRequest; + } + } + + internal bool IsMultiValueInResponse { + get { + return (_type & HttpHeaderType.MultiValueInResponse) == HttpHeaderType.MultiValueInResponse; + } + } + + #endregion + + #region Public Properties + + public bool IsRequest { + get { + return (_type & HttpHeaderType.Request) == HttpHeaderType.Request; + } + } + + public bool IsResponse { + get { + return (_type & HttpHeaderType.Response) == HttpHeaderType.Response; + } + } + + public string Name { + get; set; + } + + public HttpHeaderType Type { + get { + return _type; + } + + set { + _type = value; + } + } + + #endregion + + #region Public Methods + + public bool IsMultiValue (bool response) + { + return (_type & HttpHeaderType.MultiValue) != HttpHeaderType.MultiValue + ? response + ? IsMultiValueInResponse + : IsMultiValueInRequest + : response + ? IsResponse + : IsRequest; + } + + public bool IsRestricted (bool response) + { + return (_type & HttpHeaderType.Restricted) != HttpHeaderType.Restricted + ? false + : response + ? IsResponse + : IsRequest; + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpHeaderType.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpHeaderType.cs new file mode 100644 index 0000000..113fb63 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpHeaderType.cs @@ -0,0 +1,44 @@ +#region License +/* + * HttpHeaderType.cs + * + * The MIT License + * + * Copyright (c) 2013-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; + +namespace WebSocketSharp.Net +{ + [Flags] + internal enum HttpHeaderType + { + Unspecified = 0, + Request = 1, + Response = 1 << 1, + Restricted = 1 << 2, + MultiValue = 1 << 3, + MultiValueInRequest = 1 << 4, + MultiValueInResponse = 1 << 5 + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpListener.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpListener.cs new file mode 100644 index 0000000..bdc622b --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpListener.cs @@ -0,0 +1,743 @@ +#region License +/* + * HttpListener.cs + * + * This code is derived from System.Net.HttpListener.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; +using System.Security.Principal; +using System.Threading; + +// TODO: Logging. +namespace WebSocketSharp.Net +{ + /// + /// Provides a simple, programmatically controlled HTTP listener. + /// + public sealed class HttpListener : IDisposable + { + #region Private Fields + + private AuthenticationSchemes _authSchemes; + private Func _authSchemeSelector; + private string _certFolderPath; + private Dictionary _connections; + private List _contextQueue; + private Func _credentialsFinder; + private X509Certificate2 _defaultCert; + private bool _disposed; + private bool _ignoreWriteExceptions; + private bool _listening; + private HttpListenerPrefixCollection _prefixes; + private string _realm; + private Dictionary _registry; + private List _waitQueue; + + #endregion + + #region Public Constructors + + /// + /// Initializes a new instance of the class. + /// + public HttpListener () + { + _authSchemes = AuthenticationSchemes.Anonymous; + _connections = new Dictionary (); + _contextQueue = new List (); + _prefixes = new HttpListenerPrefixCollection (this); + _registry = new Dictionary (); + _waitQueue = new List (); + } + + #endregion + + #region Internal Properties + + internal bool IsDisposed { + get { + return _disposed; + } + } + + #endregion + + #region Public Properties + + /// + /// Gets or sets the scheme used to authenticate the clients. + /// + /// + /// One of the enum values, + /// represents the scheme used to authenticate the clients. The default value is + /// . + /// + /// + /// This listener has been closed. + /// + public AuthenticationSchemes AuthenticationSchemes { + get { + CheckDisposed (); + return _authSchemes; + } + + set { + CheckDisposed (); + _authSchemes = value; + } + } + + /// + /// Gets or sets the delegate called to select the scheme used to authenticate the clients. + /// + /// + /// + /// If you set this property, the listener uses the authentication scheme selected by + /// the delegate for each request. + /// + /// + /// If you don't set, the listener uses the value of the AuthenticationSchemes + /// property as the authentication scheme for all requests. + /// + /// + /// + /// A Func<, > + /// delegate that invokes the method(s) used to select an authentication scheme. The default + /// value is . + /// + /// + /// This listener has been closed. + /// + public Func AuthenticationSchemeSelector { + get { + CheckDisposed (); + return _authSchemeSelector; + } + + set { + CheckDisposed (); + _authSchemeSelector = value; + } + } + + /// + /// Gets or sets the path to the folder in which stores the certificate files used to + /// authenticate the server on the secure connection. + /// + /// + /// + /// This property represents the path to the folder in which stores the certificate files + /// associated with each port number of added URI prefixes. A set of the certificate files + /// is a pair of the 'port number'.cer (DER) and 'port number'.key + /// (DER, RSA Private Key). + /// + /// + /// If this property is or empty, the result of + /// System.Environment.GetFolderPath + /// () is used as the default path. + /// + /// + /// + /// A that represents the path to the folder in which stores + /// the certificate files. The default value is . + /// + /// + /// This listener has been closed. + /// + public string CertificateFolderPath { + get { + CheckDisposed (); + return _certFolderPath; + } + + set { + CheckDisposed (); + _certFolderPath = value; + } + } + + /// + /// Gets or sets the default certificate used to authenticate the server on the secure + /// connection. + /// + /// + /// A used to authenticate the server if the certificate + /// files aren't found in the . The default value is + /// . + /// + /// + /// This listener has been closed. + /// + public X509Certificate2 DefaultCertificate { + get { + CheckDisposed (); + return _defaultCert; + } + + set { + CheckDisposed (); + _defaultCert = value; + } + } + + /// + /// Gets or sets a value indicating whether the listener returns exceptions that occur when + /// sending the response to the client. + /// + /// + /// true if the listener doesn't return exceptions that occur when sending the response + /// to the client; otherwise, false. The default value is false. + /// + /// + /// This listener has been closed. + /// + public bool IgnoreWriteExceptions { + get { + CheckDisposed (); + return _ignoreWriteExceptions; + } + + set { + CheckDisposed (); + _ignoreWriteExceptions = value; + } + } + + /// + /// Gets a value indicating whether the listener has been started. + /// + /// + /// true if the listener has been started; otherwise, false. + /// + public bool IsListening { + get { + return _listening; + } + } + + /// + /// Gets a value indicating whether the listener can be used with the current operating system. + /// + /// + /// true. + /// + public static bool IsSupported { + get { + return true; + } + } + + /// + /// Gets the URI prefixes handled by the listener. + /// + /// + /// A that contains the URI prefixes. + /// + /// + /// This listener has been closed. + /// + public HttpListenerPrefixCollection Prefixes { + get { + CheckDisposed (); + return _prefixes; + } + } + + /// + /// Gets or sets the name of the realm associated with the listener. + /// + /// + /// A that represents the name of the realm. The default value is + /// SECRET AREA. + /// + /// + /// This listener has been closed. + /// + public string Realm { + get { + CheckDisposed (); + return _realm == null || _realm.Length == 0 + ? (_realm = "SECRET AREA") + : _realm; + } + + set { + CheckDisposed (); + _realm = value; + } + } + + /// + /// Gets or sets a value indicating whether, when NTLM authentication is used, + /// the authentication information of first request is used to authenticate + /// additional requests on the same connection. + /// + /// + /// This property isn't currently supported and always throws + /// a . + /// + /// + /// true if the authentication information of first request is used; + /// otherwise, false. + /// + /// + /// Any use of this property. + /// + public bool UnsafeConnectionNtlmAuthentication { + get { + throw new NotSupportedException (); + } + + set { + throw new NotSupportedException (); + } + } + + /// + /// Gets or sets the delegate called to find the credentials for an identity used to + /// authenticate a client. + /// + /// + /// A Func<, > delegate + /// that invokes the method(s) used to find the credentials. The default value is a function + /// that only returns . + /// + /// + /// This listener has been closed. + /// + public Func UserCredentialsFinder { + get { + CheckDisposed (); + return _credentialsFinder ?? (_credentialsFinder = identity => null); + } + + set { + CheckDisposed (); + _credentialsFinder = value; + } + } + + #endregion + + #region Private Methods + + private void cleanup (bool force) + { + lock (((ICollection) _registry).SyncRoot) { + if (!force) + sendServiceUnavailable (); + + cleanupContextRegistry (); + cleanupConnections (); + cleanupWaitQueue (); + } + } + + private void cleanupConnections () + { + lock (((ICollection) _connections).SyncRoot) { + if (_connections.Count == 0) + return; + + // Need to copy this since closing will call RemoveConnection. + var keys = _connections.Keys; + var conns = new HttpConnection [keys.Count]; + keys.CopyTo (conns, 0); + _connections.Clear (); + for (var i = conns.Length - 1; i >= 0; i--) + conns [i].Close (true); + } + } + + private void cleanupContextRegistry () + { + lock (((ICollection) _registry).SyncRoot) { + if (_registry.Count == 0) + return; + + // Need to copy this since closing will call UnregisterContext. + var keys = _registry.Keys; + var all = new HttpListenerContext [keys.Count]; + keys.CopyTo (all, 0); + _registry.Clear (); + for (var i = all.Length - 1; i >= 0; i--) + all [i].Connection.Close (true); + } + } + + private void cleanupWaitQueue () + { + lock (((ICollection) _waitQueue).SyncRoot) { + if (_waitQueue.Count == 0) + return; + + var ex = new ObjectDisposedException (GetType ().ToString ()); + foreach (var ares in _waitQueue) + ares.Complete (ex); + + _waitQueue.Clear (); + } + } + + private void close (bool force) + { + EndPointManager.RemoveListener (this); + cleanup (force); + } + + // Must be called with a lock on _contextQueue. + private HttpListenerContext getContextFromQueue () + { + if (_contextQueue.Count == 0) + return null; + + var context = _contextQueue [0]; + _contextQueue.RemoveAt (0); + + return context; + } + + private void sendServiceUnavailable () + { + lock (((ICollection) _contextQueue).SyncRoot) { + if (_contextQueue.Count == 0) + return; + + var contexts = _contextQueue.ToArray (); + _contextQueue.Clear (); + foreach (var context in contexts) { + var res = context.Response; + res.StatusCode = (int) HttpStatusCode.ServiceUnavailable; + res.Close (); + } + } + } + + #endregion + + #region Internal Methods + + internal void AddConnection (HttpConnection connection) + { + lock (((ICollection) _connections).SyncRoot) + _connections [connection] = connection; + } + + internal ListenerAsyncResult BeginGetContext (ListenerAsyncResult asyncResult) + { + CheckDisposed (); + if (_prefixes.Count == 0) + throw new InvalidOperationException ("Please, call AddPrefix before using this method."); + + if (!_listening) + throw new InvalidOperationException ("Please, call Start before using this method."); + + // Lock _waitQueue early to avoid race conditions. + lock (((ICollection) _waitQueue).SyncRoot) { + lock (((ICollection) _contextQueue).SyncRoot) { + var context = getContextFromQueue (); + if (context != null) { + asyncResult.Complete (context, true); + return asyncResult; + } + } + + _waitQueue.Add (asyncResult); + } + + return asyncResult; + } + + internal void CheckDisposed () + { + if (_disposed) + throw new ObjectDisposedException (GetType ().ToString ()); + } + + internal void RegisterContext (HttpListenerContext context) + { + lock (((ICollection) _registry).SyncRoot) + _registry [context] = context; + + ListenerAsyncResult ares = null; + lock (((ICollection) _waitQueue).SyncRoot) { + if (_waitQueue.Count == 0) { + lock (((ICollection) _contextQueue).SyncRoot) + _contextQueue.Add (context); + } + else { + ares = _waitQueue [0]; + _waitQueue.RemoveAt (0); + } + } + + if (ares != null) + ares.Complete (context); + } + + internal void RemoveConnection (HttpConnection connection) + { + lock (((ICollection) _connections).SyncRoot) + _connections.Remove (connection); + } + + internal AuthenticationSchemes SelectAuthenticationScheme (HttpListenerContext context) + { + return AuthenticationSchemeSelector != null + ? AuthenticationSchemeSelector (context.Request) + : _authSchemes; + } + + internal void UnregisterContext (HttpListenerContext context) + { + lock (((ICollection) _registry).SyncRoot) + _registry.Remove (context); + + lock (((ICollection) _contextQueue).SyncRoot) { + var i = _contextQueue.IndexOf (context); + if (i >= 0) + _contextQueue.RemoveAt (i); + } + } + + #endregion + + #region Public Methods + + /// + /// Shuts down the listener immediately. + /// + public void Abort () + { + if (_disposed) + return; + + close (true); + _disposed = true; + } + + /// + /// Begins getting an incoming request information asynchronously. + /// + /// + /// This asynchronous operation must be completed by calling the EndGetContext method. + /// Typically, that method is invoked by the delegate. + /// + /// + /// An that represents the status of the asynchronous operation. + /// + /// + /// An delegate that references the method(s) to invoke + /// when the asynchronous operation completes. + /// + /// + /// An that contains a user defined object to pass to + /// the delegate. + /// + /// + /// + /// This listener doesn't have any URI prefixes to listen on. + /// + /// + /// -or- + /// + /// + /// This listener hasn't been started or is stopped currently. + /// + /// + /// + /// This listener has been closed. + /// + public IAsyncResult BeginGetContext (AsyncCallback callback, Object state) + { + return BeginGetContext (new ListenerAsyncResult (callback, state)); + } + + /// + /// Shuts down the listener. + /// + public void Close () + { + if (_disposed) + return; + + close (false); + _disposed = true; + } + + /// + /// Ends an asynchronous operation to get an incoming request information. + /// + /// + /// This method completes an asynchronous operation started by calling + /// the BeginGetContext method. + /// + /// + /// A that contains a request information. + /// + /// + /// An obtained by calling the BeginGetContext method. + /// + /// + /// is . + /// + /// + /// wasn't obtained by calling the BeginGetContext method. + /// + /// + /// This method was already called for the specified . + /// + /// + /// This listener has been closed. + /// + public HttpListenerContext EndGetContext (IAsyncResult asyncResult) + { + CheckDisposed (); + if (asyncResult == null) + throw new ArgumentNullException ("asyncResult"); + + var ares = asyncResult as ListenerAsyncResult; + if (ares == null) + throw new ArgumentException ("Wrong IAsyncResult.", "asyncResult"); + + if (ares.EndCalled) + throw new InvalidOperationException ("Cannot reuse this IAsyncResult."); + + ares.EndCalled = true; + if (!ares.IsCompleted) + ares.AsyncWaitHandle.WaitOne (); + + lock (((ICollection) _waitQueue).SyncRoot) { + var i = _waitQueue.IndexOf (ares); + if (i >= 0) + _waitQueue.RemoveAt (i); + } + + var context = ares.GetContext (); + var scheme = SelectAuthenticationScheme (context); + if (scheme != AuthenticationSchemes.Anonymous) + context.SetUser (scheme, Realm, UserCredentialsFinder); + + return context; // This will throw on error. + } + + /// + /// Gets an incoming request information. + /// + /// + /// This method waits for an incoming request and returns a request information + /// when the listener receives a request. + /// + /// + /// A that contains a request information. + /// + /// + /// + /// This listener doesn't have any URI prefixes to listen on. + /// + /// + /// -or- + /// + /// + /// This listener hasn't been started or is stopped currently. + /// + /// + /// + /// This listener has been closed. + /// + public HttpListenerContext GetContext () + { + var ares = BeginGetContext (new ListenerAsyncResult (null, null)); + ares.InGet = true; + + return EndGetContext (ares); + } + + /// + /// Starts to receive incoming requests. + /// + /// + /// This listener has been closed. + /// + public void Start () + { + CheckDisposed (); + if (_listening) + return; + + EndPointManager.AddListener (this); + _listening = true; + } + + /// + /// Stops receiving incoming requests. + /// + /// + /// This listener has been closed. + /// + public void Stop () + { + CheckDisposed (); + if (!_listening) + return; + + _listening = false; + EndPointManager.RemoveListener (this); + sendServiceUnavailable (); + } + + #endregion + + #region Explicit Interface Implementation + + /// + /// Releases all resource used by the listener. + /// + void IDisposable.Dispose () + { + if (_disposed) + return; + + close (true); // TODO: Should we force here or not? + _disposed = true; + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpListenerContext.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpListenerContext.cs new file mode 100644 index 0000000..7b37a27 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpListenerContext.cs @@ -0,0 +1,239 @@ +#region License +/* + * HttpListenerContext.cs + * + * This code is derived from System.Net.HttpListenerContext.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; +using System.Security.Principal; +using WebSocketSharp.Net.WebSockets; + +namespace WebSocketSharp.Net +{ + /// + /// Provides a set of methods and properties used to access the HTTP request and response + /// information used by the . + /// + /// + /// The HttpListenerContext class cannot be inherited. + /// + public sealed class HttpListenerContext + { + #region Private Fields + + private HttpConnection _connection; + private string _error; + private int _errorStatus; + private HttpListenerRequest _request; + private HttpListenerResponse _response; + private IPrincipal _user; + + #endregion + + #region Internal Fields + + internal HttpListener Listener; + + #endregion + + #region Internal Constructors + + internal HttpListenerContext (HttpConnection connection) + { + _connection = connection; + _errorStatus = 400; + _request = new HttpListenerRequest (this); + _response = new HttpListenerResponse (this); + } + + #endregion + + #region Internal Properties + + internal HttpConnection Connection { + get { + return _connection; + } + } + + internal string ErrorMessage { + get { + return _error; + } + + set { + _error = value; + } + } + + internal int ErrorStatus { + get { + return _errorStatus; + } + + set { + _errorStatus = value; + } + } + + internal bool HasError { + get { + return _error != null; + } + } + + #endregion + + #region Public Properties + + /// + /// Gets the HTTP request information from a client. + /// + /// + /// A that represents the HTTP request. + /// + public HttpListenerRequest Request { + get { + return _request; + } + } + + /// + /// Gets the HTTP response information used to send to the client. + /// + /// + /// A that represents the HTTP response to send. + /// + public HttpListenerResponse Response { + get { + return _response; + } + } + + /// + /// Gets the client information (identity, authentication, and security roles). + /// + /// + /// A that represents the client information. + /// + public IPrincipal User { + get { + return _user; + } + } + + #endregion + + #region Internal Methods + + internal void SetUser ( + AuthenticationSchemes scheme, + string realm, + Func credentialsFinder) + { + var authRes = AuthenticationResponse.Parse (_request.Headers ["Authorization"]); + if (authRes == null) + return; + + var id = authRes.ToIdentity (); + if (id == null) + return; + + NetworkCredential cred = null; + try { + cred = credentialsFinder (id); + } + catch { + } + + if (cred == null) + return; + + var valid = scheme == AuthenticationSchemes.Basic + ? ((HttpBasicIdentity) id).Password == cred.Password + : scheme == AuthenticationSchemes.Digest + ? ((HttpDigestIdentity) id).IsValid ( + cred.Password, realm, _request.HttpMethod, null) + : false; + + if (valid) + _user = new GenericPrincipal (id, cred.Roles); + } + + #endregion + + #region Public Methods + + /// + /// Accepts a WebSocket connection request. + /// + /// + /// A that represents the WebSocket connection + /// request. + /// + /// + /// A that represents the subprotocol used in the WebSocket connection. + /// + /// + /// A that provides the logging functions used in the WebSocket attempts. + /// + /// + /// + /// is empty. + /// + /// + /// -or- + /// + /// + /// contains an invalid character. + /// + /// + public HttpListenerWebSocketContext AcceptWebSocket (string protocol, Logger logger) + { + if (protocol != null) { + if (protocol.Length == 0) + throw new ArgumentException ("An empty string.", "protocol"); + + if (!protocol.IsToken ()) + throw new ArgumentException ("Contains an invalid character.", "protocol"); + } + + return new HttpListenerWebSocketContext (this, protocol, logger ?? new Logger ()); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpListenerException.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpListenerException.cs new file mode 100644 index 0000000..a52eeec --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpListenerException.cs @@ -0,0 +1,127 @@ +#region License +/* + * HttpListenerException.cs + * + * This code is derived from System.Net.HttpListenerException.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; +using System.ComponentModel; +using System.Runtime.Serialization; + +namespace WebSocketSharp.Net +{ + /// + /// The exception that is thrown when a gets an error + /// processing an HTTP request. + /// + [Serializable] + public class HttpListenerException : Win32Exception + { + #region Protected Constructors + + /// + /// Initializes a new instance of the class from + /// the specified and . + /// + /// + /// A that contains the serialized object data. + /// + /// + /// A that specifies the source for the deserialization. + /// + protected HttpListenerException ( + SerializationInfo serializationInfo, StreamingContext streamingContext) + : base (serializationInfo, streamingContext) + { + } + + #endregion + + #region Public Constructors + + /// + /// Initializes a new instance of the class. + /// + public HttpListenerException () + { + } + + /// + /// Initializes a new instance of the class + /// with the specified . + /// + /// + /// An that identifies the error. + /// + public HttpListenerException (int errorCode) + : base (errorCode) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified and . + /// + /// + /// An that identifies the error. + /// + /// + /// A that describes the error. + /// + public HttpListenerException (int errorCode, string message) + : base (errorCode, message) + { + } + + #endregion + + #region Public Properties + + /// + /// Gets the error code that identifies the error that occurred. + /// + /// + /// An that identifies the error. + /// + public override int ErrorCode { + get { + return NativeErrorCode; + } + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpListenerPrefixCollection.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpListenerPrefixCollection.cs new file mode 100644 index 0000000..170ac3c --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpListenerPrefixCollection.cs @@ -0,0 +1,304 @@ +#region License +/* + * HttpListenerPrefixCollection.cs + * + * This code is derived from System.Net.HttpListenerPrefixCollection.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace WebSocketSharp.Net +{ + /// + /// Provides the collection used to store the URI prefixes for the . + /// + /// + /// The responds to the request which has a requested URI that + /// the prefixes most closely match. + /// + public class HttpListenerPrefixCollection + : ICollection, IEnumerable, IEnumerable + { + #region Private Fields + + private HttpListener _listener; + private List _prefixes; + + #endregion + + #region Private Constructors + + private HttpListenerPrefixCollection () + { + _prefixes = new List (); + } + + #endregion + + #region Internal Constructors + + internal HttpListenerPrefixCollection (HttpListener listener) + : this () + { + _listener = listener; + } + + #endregion + + #region Public Properties + + /// + /// Gets the number of prefixes contained in the . + /// + /// + /// An that represents the number of prefixes. + /// + public int Count { + get { + return _prefixes.Count; + } + } + + /// + /// Gets a value indicating whether the access to the + /// is read-only. + /// + /// + /// Always returns false. + /// + public bool IsReadOnly { + get { + return false; + } + } + + /// + /// Gets a value indicating whether the access to the + /// is synchronized. + /// + /// + /// Always returns false. + /// + public bool IsSynchronized { + get { + return false; + } + } + + #endregion + + #region Public Methods + + /// + /// Adds the specified to + /// the . + /// + /// + /// A that represents the URI prefix to add. The prefix must be + /// a well-formed URI prefix with http or https scheme, and must be terminated with + /// a "/". + /// + /// + /// is . + /// + /// + /// is invalid. + /// + /// + /// The associated with + /// this is closed. + /// + public void Add (string uriPrefix) + { + _listener.CheckDisposed (); + ListenerPrefix.CheckUriPrefix (uriPrefix); + if (_prefixes.Contains (uriPrefix)) + return; + + _prefixes.Add (uriPrefix); + if (_listener.IsListening) + EndPointManager.AddPrefix (uriPrefix, _listener); + } + + /// + /// Removes all URI prefixes from the . + /// + /// + /// The associated with + /// this is closed. + /// + public void Clear () + { + _listener.CheckDisposed (); + _prefixes.Clear (); + if (_listener.IsListening) + EndPointManager.RemoveListener (_listener); + } + + /// + /// Returns a value indicating whether the contains + /// the specified . + /// + /// + /// true if the contains + /// ; otherwise, false. + /// + /// + /// A that represents the URI prefix to test. + /// + /// + /// is . + /// + /// + /// The associated with + /// this is closed. + /// + public bool Contains (string uriPrefix) + { + _listener.CheckDisposed (); + if (uriPrefix == null) + throw new ArgumentNullException ("uriPrefix"); + + return _prefixes.Contains (uriPrefix); + } + + /// + /// Copies the contents of the to + /// the specified . + /// + /// + /// An that receives the URI prefix strings in + /// the . + /// + /// + /// An that represents the zero-based index in + /// at which copying begins. + /// + /// + /// The associated with + /// this is closed. + /// + public void CopyTo (Array array, int offset) + { + _listener.CheckDisposed (); + ((ICollection) _prefixes).CopyTo (array, offset); + } + + /// + /// Copies the contents of the to + /// the specified array of . + /// + /// + /// An array of that receives the URI prefix strings in + /// the . + /// + /// + /// An that represents the zero-based index in + /// at which copying begins. + /// + /// + /// The associated with + /// this is closed. + /// + public void CopyTo (string [] array, int offset) + { + _listener.CheckDisposed (); + _prefixes.CopyTo (array, offset); + } + + /// + /// Gets the enumerator used to iterate through the . + /// + /// + /// An instance used to iterate + /// through the . + /// + public IEnumerator GetEnumerator () + { + return _prefixes.GetEnumerator (); + } + + /// + /// Removes the specified from the list of prefixes in + /// the . + /// + /// + /// true if is successfully found and removed; + /// otherwise, false. + /// + /// + /// A that represents the URI prefix to remove. + /// + /// + /// is . + /// + /// + /// The associated with + /// this is closed. + /// + public bool Remove (string uriPrefix) + { + _listener.CheckDisposed (); + if (uriPrefix == null) + throw new ArgumentNullException ("uriPrefix"); + + var result = _prefixes.Remove (uriPrefix); + if (result && _listener.IsListening) + EndPointManager.RemovePrefix (uriPrefix, _listener); + + return result; + } + + #endregion + + #region Explicit Interface Implementation + + /// + /// Gets the enumerator used to iterate through the . + /// + /// + /// An instance used to iterate through + /// the . + /// + IEnumerator IEnumerable.GetEnumerator () + { + return _prefixes.GetEnumerator (); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpListenerRequest.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpListenerRequest.cs new file mode 100644 index 0000000..f461201 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpListenerRequest.cs @@ -0,0 +1,727 @@ +#region License +/* + * HttpListenerRequest.cs + * + * This code is derived from System.Net.HttpListenerRequest.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Globalization; +using System.IO; +using System.Security.Cryptography.X509Certificates; +using System.Text; + +namespace WebSocketSharp.Net +{ + /// + /// Provides the access to a request to the . + /// + /// + /// The HttpListenerRequest class cannot be inherited. + /// + public sealed class HttpListenerRequest + { + #region Private Static Fields + + private static byte [] _100continue = + Encoding.ASCII.GetBytes ("HTTP/1.1 100 Continue\r\n\r\n"); + + #endregion + + #region Private Fields + + private string [] _acceptTypes; + private bool _chunked; + private Encoding _contentEncoding; + private long _contentLength; + private bool _contentLengthWasSet; + private HttpListenerContext _context; + private CookieCollection _cookies; + private WebHeaderCollection _headers; + private Guid _identifier; + private Stream _inputStream; + private bool _keepAlive; + private bool _keepAliveWasSet; + private string _method; + private NameValueCollection _queryString; + private Uri _referer; + private string _uri; + private Uri _url; + private string [] _userLanguages; + private Version _version; + private bool _websocketRequest; + private bool _websocketRequestWasSet; + + #endregion + + #region Internal Constructors + + internal HttpListenerRequest (HttpListenerContext context) + { + _context = context; + _contentLength = -1; + _headers = new WebHeaderCollection (); + _identifier = Guid.NewGuid (); + } + + #endregion + + #region Public Properties + + /// + /// Gets the media types which are acceptable for the response. + /// + /// + /// An array of that contains the media type names in the Accept + /// request-header, or if the request didn't include an Accept header. + /// + public string [] AcceptTypes { + get { + return _acceptTypes; + } + } + + /// + /// Gets an error code that identifies a problem with the client's certificate. + /// + /// + /// Always returns 0. + /// + public int ClientCertificateError { + get { + // TODO: Always returns 0. + return 0; + } + } + + /// + /// Gets the encoding for the entity body data included in the request. + /// + /// + /// A that represents the encoding for the entity body data, or + /// if the request didn't include the information about + /// the encoding. + /// + public Encoding ContentEncoding { + get { + return _contentEncoding ?? (_contentEncoding = Encoding.Default); + } + } + + /// + /// Gets the size of the entity body data included in the request. + /// + /// + /// A that represents the value of the Content-Length entity-header. The + /// value is a number of bytes in the entity body data. -1 if the size isn't known. + /// + public long ContentLength64 { + get { + return _contentLength; + } + } + + /// + /// Gets the media type of the entity body included in the request. + /// + /// + /// A that represents the value of the Content-Type entity-header. + /// + public string ContentType { + get { + return _headers ["Content-Type"]; + } + } + + /// + /// Gets the cookies included in the request. + /// + /// + /// A that contains the cookies included in the request. + /// + public CookieCollection Cookies { + get { + return _cookies ?? (_cookies = _headers.GetCookies (false)); + } + } + + /// + /// Gets a value indicating whether the request has the entity body. + /// + /// + /// true if the request has the entity body; otherwise, false. + /// + public bool HasEntityBody { + get { + return _contentLength > 0 || _chunked; + } + } + + /// + /// Gets the HTTP headers used in the request. + /// + /// + /// A that contains the HTTP headers used in the request. + /// + public NameValueCollection Headers { + get { + return _headers; + } + } + + /// + /// Gets the HTTP method used in the request. + /// + /// + /// A that represents the HTTP method used in the request. + /// + public string HttpMethod { + get { + return _method; + } + } + + /// + /// Gets a that contains the entity body data included in the request. + /// + /// + /// A that contains the entity body data included in the request. + /// + public Stream InputStream { + get { + return _inputStream ?? + (_inputStream = HasEntityBody + ? _context.Connection.GetRequestStream (_chunked, _contentLength) + : Stream.Null); + } + } + + /// + /// Gets a value indicating whether the client that sent the request is authenticated. + /// + /// + /// true if the client is authenticated; otherwise, false. + /// + public bool IsAuthenticated { + get { + var user = _context.User; + return user != null && user.Identity.IsAuthenticated; + } + } + + /// + /// Gets a value indicating whether the request is sent from the local computer. + /// + /// + /// true if the request is sent from the local computer; otherwise, false. + /// + public bool IsLocal { + get { + return RemoteEndPoint.Address.IsLocal (); + } + } + + /// + /// Gets a value indicating whether the HTTP connection is secured using the SSL protocol. + /// + /// + /// true if the HTTP connection is secured; otherwise, false. + /// + public bool IsSecureConnection { + get { + return _context.Connection.IsSecure; + } + } + + /// + /// Gets a value indicating whether the request is a WebSocket connection request. + /// + /// + /// true if the request is a WebSocket connection request; otherwise, false. + /// + public bool IsWebSocketRequest { + get { + if (!_websocketRequestWasSet) { + _websocketRequest = _method == "GET" && + _version > HttpVersion.Version10 && + _headers.Contains ("Upgrade", "websocket") && + _headers.Contains ("Connection", "Upgrade"); + + _websocketRequestWasSet = true; + } + + return _websocketRequest; + } + } + + /// + /// Gets a value indicating whether the client requests a persistent connection. + /// + /// + /// true if the client requests a persistent connection; otherwise, false. + /// + public bool KeepAlive { + get { + if (!_keepAliveWasSet) { + string keepAlive; + _keepAlive = _version > HttpVersion.Version10 || + _headers.Contains ("Connection", "keep-alive") || + ((keepAlive = _headers ["Keep-Alive"]) != null && keepAlive != "closed"); + + _keepAliveWasSet = true; + } + + return _keepAlive; + } + } + + /// + /// Gets the server endpoint as an IP address and a port number. + /// + /// + /// A that represents the server endpoint. + /// + public System.Net.IPEndPoint LocalEndPoint { + get { + return _context.Connection.LocalEndPoint; + } + } + + /// + /// Gets the HTTP version used in the request. + /// + /// + /// A that represents the HTTP version used in the request. + /// + public Version ProtocolVersion { + get { + return _version; + } + } + + /// + /// Gets the query string included in the request. + /// + /// + /// A that contains the query string parameters. + /// + public NameValueCollection QueryString { + get { + return _queryString ?? + (_queryString = HttpUtility.ParseQueryStringInternally (_url.Query, Encoding.UTF8)); + } + } + + /// + /// Gets the raw URL (without the scheme, host, and port) requested by the client. + /// + /// + /// A that represents the raw URL requested by the client. + /// + public string RawUrl { + get { + // TODO: Should decode? + return _url.PathAndQuery; + } + } + + /// + /// Gets the client endpoint as an IP address and a port number. + /// + /// + /// A that represents the client endpoint. + /// + public System.Net.IPEndPoint RemoteEndPoint { + get { + return _context.Connection.RemoteEndPoint; + } + } + + /// + /// Gets the request identifier of a incoming HTTP request. + /// + /// + /// A that represents the identifier of a request. + /// + public Guid RequestTraceIdentifier { + get { + return _identifier; + } + } + + /// + /// Gets the URL requested by the client. + /// + /// + /// A that represents the URL requested by the client. + /// + public Uri Url { + get { + return _url; + } + } + + /// + /// Gets the URL of the resource from which the requested URL was obtained. + /// + /// + /// A that represents the value of the Referer request-header, + /// or if the request didn't include an Referer header. + /// + public Uri UrlReferrer { + get { + return _referer; + } + } + + /// + /// Gets the information about the user agent originating the request. + /// + /// + /// A that represents the value of the User-Agent request-header. + /// + public string UserAgent { + get { + return _headers ["User-Agent"]; + } + } + + /// + /// Gets the server endpoint as an IP address and a port number. + /// + /// + /// A that represents the server endpoint. + /// + public string UserHostAddress { + get { + return LocalEndPoint.ToString (); + } + } + + /// + /// Gets the internet host name and port number (if present) specified by the client. + /// + /// + /// A that represents the value of the Host request-header. + /// + public string UserHostName { + get { + return _headers ["Host"]; + } + } + + /// + /// Gets the natural languages which are preferred for the response. + /// + /// + /// An array of that contains the natural language names in + /// the Accept-Language request-header, or if the request + /// didn't include an Accept-Language header. + /// + public string [] UserLanguages { + get { + return _userLanguages; + } + } + + #endregion + + #region Private Methods + + private static bool tryCreateVersion (string version, out Version result) + { + result = null; + try { + result = new Version (version); + return true; + } + catch { + return false; + } + } + + #endregion + + #region Internal Methods + + internal void AddHeader (string header) + { + var colon = header.IndexOf (':'); + if (colon == -1) { + _context.ErrorMessage = "Invalid header"; + return; + } + + var name = header.Substring (0, colon).Trim (); + var val = header.Substring (colon + 1).Trim (); + _headers.SetInternally (name, val, false); + + var lower = name.ToLower (CultureInfo.InvariantCulture); + if (lower == "accept") { + _acceptTypes = new List (val.SplitHeaderValue (',')).ToArray (); + return; + } + + if (lower == "accept-language") { + _userLanguages = val.Split (','); + return; + } + + if (lower == "content-length") { + long length; + if (Int64.TryParse (val, out length) && length >= 0) { + _contentLength = length; + _contentLengthWasSet = true; + } + else { + _context.ErrorMessage = "Invalid Content-Length header"; + } + + return; + } + + if (lower == "content-type") { + var contents = val.Split (';'); + foreach (var content in contents) { + var tmp = content.Trim (); + if (tmp.StartsWith ("charset")) { + var charset = tmp.GetValue ("="); + if (charset != null && charset.Length > 0) { + try { + _contentEncoding = Encoding.GetEncoding (charset); + } + catch { + _context.ErrorMessage = "Invalid Content-Type header"; + } + } + + break; + } + } + + return; + } + + if (lower == "referer") + _referer = val.ToUri (); + } + + internal void FinishInitialization () + { + var host = _headers ["Host"]; + var noHost = host == null || host.Length == 0; + if (_version > HttpVersion.Version10 && noHost) { + _context.ErrorMessage = "Invalid Host header"; + return; + } + + if (noHost) + host = UserHostAddress; + + _url = HttpUtility.CreateRequestUrl (_uri, host, IsWebSocketRequest, IsSecureConnection); + if (_url == null) { + _context.ErrorMessage = "Invalid request url"; + return; + } + + var encoding = Headers ["Transfer-Encoding"]; + if (_version > HttpVersion.Version10 && encoding != null && encoding.Length > 0) { + _chunked = encoding.ToLower () == "chunked"; + if (!_chunked) { + _context.ErrorMessage = String.Empty; + _context.ErrorStatus = 501; + + return; + } + } + + if (!_chunked && !_contentLengthWasSet) { + var method = _method.ToLower (); + if (method == "post" || method == "put") { + _context.ErrorMessage = String.Empty; + _context.ErrorStatus = 411; + + return; + } + } + + var expect = Headers ["Expect"]; + if (expect != null && expect.Length > 0 && expect.ToLower () == "100-continue") { + var output = _context.Connection.GetResponseStream (); + output.WriteInternally (_100continue, 0, _100continue.Length); + } + } + + // Returns true is the stream could be reused. + internal bool FlushInput () + { + if (!HasEntityBody) + return true; + + var length = 2048; + if (_contentLength > 0) + length = (int) Math.Min (_contentLength, (long) length); + + var buff = new byte [length]; + while (true) { + // TODO: Test if MS has a timeout when doing this. + try { + var ares = InputStream.BeginRead (buff, 0, length, null, null); + if (!ares.IsCompleted && !ares.AsyncWaitHandle.WaitOne (100)) + return false; + + if (InputStream.EndRead (ares) <= 0) + return true; + } + catch { + return false; + } + } + } + + internal void SetRequestLine (string requestLine) + { + var parts = requestLine.Split (new [] { ' ' }, 3); + if (parts.Length != 3) { + _context.ErrorMessage = "Invalid request line (parts)"; + return; + } + + _method = parts [0]; + if (!_method.IsToken ()) { + _context.ErrorMessage = "Invalid request line (method)"; + return; + } + + _uri = parts [1]; + + if (parts [2].Length != 8 || + !parts [2].StartsWith ("HTTP/") || + !tryCreateVersion (parts [2].Substring (5), out _version) || + _version.Major < 1) + _context.ErrorMessage = "Invalid request line (version)"; + } + + #endregion + + #region Public Methods + + /// + /// Begins getting the client's X.509 v.3 certificate asynchronously. + /// + /// + /// This asynchronous operation must be completed by calling the + /// method. Typically, that method is invoked by the + /// delegate. + /// + /// + /// An that contains the status of the asynchronous operation. + /// + /// + /// An delegate that references the method(s) called when the + /// asynchronous operation completes. + /// + /// + /// An that contains a user defined object to pass to the + /// delegate. + /// + /// + /// This method isn't implemented. + /// + public IAsyncResult BeginGetClientCertificate (AsyncCallback requestCallback, object state) + { + // TODO: Not Implemented. + throw new NotImplementedException (); + } + + /// + /// Ends an asynchronous operation to get the client's X.509 v.3 certificate. + /// + /// + /// This method completes an asynchronous operation started by calling the + /// method. + /// + /// + /// A that contains the client's X.509 v.3 certificate. + /// + /// + /// An obtained by calling the + /// method. + /// + /// + /// This method isn't implemented. + /// + public X509Certificate2 EndGetClientCertificate (IAsyncResult asyncResult) + { + // TODO: Not Implemented. + throw new NotImplementedException (); + } + + /// + /// Gets the client's X.509 v.3 certificate. + /// + /// + /// A that contains the client's X.509 v.3 certificate. + /// + /// + /// This method isn't implemented. + /// + public X509Certificate2 GetClientCertificate () + { + // TODO: Not Implemented. + throw new NotImplementedException (); + } + + /// + /// Returns a that represents the current + /// . + /// + /// + /// A that represents the current . + /// + public override string ToString () + { + var buff = new StringBuilder (64); + buff.AppendFormat ("{0} {1} HTTP/{2}\r\n", _method, _uri, _version); + buff.Append (_headers.ToString ()); + + return buff.ToString (); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpListenerResponse.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpListenerResponse.cs new file mode 100644 index 0000000..20af4f4 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpListenerResponse.cs @@ -0,0 +1,906 @@ +#region License +/* + * HttpListenerResponse.cs + * + * This code is derived from System.Net.HttpListenerResponse.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; + +namespace WebSocketSharp.Net +{ + /// + /// Provides the access to a response to a request received by the . + /// + /// + /// The HttpListenerResponse class cannot be inherited. + /// + public sealed class HttpListenerResponse : IDisposable + { + #region Private Fields + + private bool _chunked; + private Encoding _contentEncoding; + private long _contentLength; + private bool _contentLengthSet; + private string _contentType; + private HttpListenerContext _context; + private CookieCollection _cookies; + private bool _disposed; + private bool _forceCloseChunked; + private WebHeaderCollection _headers; + private bool _headersSent; + private bool _keepAlive; + private string _location; + private ResponseStream _outputStream; + private int _statusCode; + private string _statusDescription; + private Version _version; + + #endregion + + #region Internal Constructors + + internal HttpListenerResponse (HttpListenerContext context) + { + _context = context; + _headers = new WebHeaderCollection (); + _keepAlive = true; + _statusCode = 200; + _statusDescription = "OK"; + _version = HttpVersion.Version11; + } + + #endregion + + #region Internal Properties + + internal bool CloseConnection { + get { + return _headers ["Connection"] == "close"; + } + } + + internal bool ForceCloseChunked { + get { + return _forceCloseChunked; + } + } + + internal bool HeadersSent { + get { + return _headersSent; + } + } + + #endregion + + #region Public Properties + + /// + /// Gets or sets the encoding for the entity body data included in the response. + /// + /// + /// A that represents the encoding for the entity body data, or + /// if no encoding is specified. + /// + /// + /// The response has already been sent. + /// + /// + /// This object is closed. + /// + public Encoding ContentEncoding { + get { + checkDisposed (); + return _contentEncoding; + } + + set { + checkDisposedOrHeadersSent (); + _contentEncoding = value; + } + } + + /// + /// Gets or sets the size of the entity body data included in the response. + /// + /// + /// A that represents the value of the Content-Length entity-header field. + /// The value is a number of bytes in the entity body data. + /// + /// + /// The value specified for a set operation is less than zero. + /// + /// + /// The response has already been sent. + /// + /// + /// This object is closed. + /// + public long ContentLength64 { + get { + checkDisposed (); + return _contentLength; + } + + set { + checkDisposedOrHeadersSent (); + if (value < 0) + throw new ArgumentOutOfRangeException ("Less than zero.", "value"); + + _contentLengthSet = true; + _contentLength = value; + } + } + + /// + /// Gets or sets the media type of the entity body included in the response. + /// + /// + /// The type of the content. A that represents the value of + /// the Content-Type entity-header field. + /// + /// + /// The value specified for a set operation is empty. + /// + /// + /// The value specified for a set operation is . + /// + /// + /// The response has already been sent. + /// + /// + /// This object is closed. + /// + public string ContentType { + get { + checkDisposed (); + return _contentType; + } + + set { + checkDisposedOrHeadersSent (); + if (value == null) + throw new ArgumentNullException ("value"); + + if (value.Length == 0) + throw new ArgumentException ("An empty string.", "value"); + + _contentType = value; + } + } + + /// + /// Gets or sets the cookies returned with the response. + /// + /// + /// A that contains the cookies returned with the response. + /// + /// + /// The response has already been sent. + /// + /// + /// This object is closed. + /// + public CookieCollection Cookies { + get { + checkDisposed (); + return _cookies ?? (_cookies = new CookieCollection ()); + } + + set { + checkDisposedOrHeadersSent (); + _cookies = value; + } + } + + /// + /// Gets or sets the HTTP headers returned to the client. + /// + /// + /// A that contains the headers returned to the client. + /// + /// + /// The value specified for a set operation is . + /// + /// + /// The response has already been sent. + /// + /// + /// This object is closed. + /// + public WebHeaderCollection Headers { + get { + checkDisposed (); + return _headers; + } + + set { + /* + * "If you attempt to set a Content-Length, Keep-Alive, Transfer-Encoding, + * or WWW-Authenticate header using the Headers property, an exception + * will be thrown. Use the KeepAlive or ContentLength64 properties to set + * these headers. You cannot set the Transfer-Encoding or WWW-Authenticate + * headers manually." + */ + + // TODO: Check if this is marked readonly after headers are sent. + + checkDisposedOrHeadersSent (); + if (value == null) + throw new ArgumentNullException ("value"); + + _headers = value; + } + } + + /// + /// Gets or sets a value indicating whether the server requests a persistent connection. + /// + /// + /// true if the server requests a persistent connection; otherwise, false. + /// The default value is true. + /// + /// + /// The response has already been sent. + /// + /// + /// This object is closed. + /// + public bool KeepAlive { + get { + checkDisposed (); + return _keepAlive; + } + + set { + checkDisposedOrHeadersSent (); + _keepAlive = value; + } + } + + /// + /// Gets a to use to write the entity body data. + /// + /// + /// A to use to write the entity body data. + /// + /// + /// This object is closed. + /// + public Stream OutputStream { + get { + checkDisposed (); + return _outputStream ?? (_outputStream = _context.Connection.GetResponseStream ()); + } + } + + /// + /// Gets or sets the HTTP version used in the response. + /// + /// + /// A that represents the HTTP version used in the response. + /// + /// + /// The value specified for a set operation doesn't have its Major property set to 1 or + /// doesn't have its Minor property set to either 0 or 1. + /// + /// + /// The value specified for a set operation is . + /// + /// + /// The response has already been sent. + /// + /// + /// This object is closed. + /// + public Version ProtocolVersion { + get { + checkDisposed (); + return _version; + } + + set { + checkDisposedOrHeadersSent (); + if (value == null) + throw new ArgumentNullException ("value"); + + if (value.Major != 1 || (value.Minor != 0 && value.Minor != 1)) + throw new ArgumentException ("Neither 1.0 nor 1.1.", "value"); + + _version = value; + } + } + + /// + /// Gets or sets the URL to which the client is redirected to locate a requested resource. + /// + /// + /// A that represents the value of the Location response-header field. + /// + /// + /// The value specified for a set operation is empty. + /// + /// + /// The response has already been sent. + /// + /// + /// This object is closed. + /// + public string RedirectLocation { + get { + checkDisposed (); + return _location; + } + + set { + checkDisposedOrHeadersSent (); + if (value.Length == 0) + throw new ArgumentException ("An empty string.", "value"); + + _location = value; + } + } + + /// + /// Gets or sets a value indicating whether the response uses the chunked transfer encoding. + /// + /// + /// true if the response uses the chunked transfer encoding; otherwise, false. + /// + /// + /// The response has already been sent. + /// + /// + /// This object is closed. + /// + public bool SendChunked { + get { + return _chunked; + } + + set { + checkDisposedOrHeadersSent (); + _chunked = value; + } + } + + /// + /// Gets or sets the HTTP status code returned to the client. + /// + /// + /// An that represents the HTTP status code for the response to the request. + /// The default value is . + /// + /// + /// The response has already been sent. + /// + /// + /// The value specified for a set operation is invalid. Valid values are between 100 and 999. + /// + /// + /// This object is closed. + /// + public int StatusCode { + get { + checkDisposed (); + return _statusCode; + } + + set { + checkDisposedOrHeadersSent (); + if (value < 100 || value > 999) + throw new System.Net.ProtocolViolationException ( + "StatusCode must be between 100 and 999."); + + _statusCode = value; + _statusDescription = value.GetStatusDescription (); + } + } + + /// + /// Gets or sets the description of the HTTP status code returned to the client. + /// + /// + /// A that represents the description of the HTTP status code. + /// + /// + /// The response has already been sent. + /// + /// + /// This object is closed. + /// + public string StatusDescription { + get { + checkDisposed (); + return _statusDescription; + } + + set { + checkDisposedOrHeadersSent (); + _statusDescription = value == null || value.Length == 0 + ? _statusCode.GetStatusDescription () + : value; + } + } + + #endregion + + #region Private Methods + + private bool canAddOrUpdate (Cookie cookie) + { + if (_cookies == null || _cookies.Count == 0) + return true; + + var found = findCookie (cookie).ToList (); + if (found.Count == 0) + return true; + + var version = cookie.Version; + foreach (var c in found) + if (c.Version == version) + return true; + + return false; + } + + private void checkDisposed () + { + if (_disposed) + throw new ObjectDisposedException (GetType ().ToString ()); + } + + private void checkDisposedOrHeadersSent () + { + if (_disposed) + throw new ObjectDisposedException (GetType ().ToString ()); + + if (_headersSent) + throw new InvalidOperationException ("Cannot be changed after headers are sent."); + } + + private void close (bool force) + { + _disposed = true; + _context.Connection.Close (force); + } + + private IEnumerable findCookie (Cookie cookie) + { + var name = cookie.Name; + var domain = cookie.Domain; + var path = cookie.Path; + if (_cookies != null) + foreach (Cookie c in _cookies) + if (c.Name.Equals (name, StringComparison.OrdinalIgnoreCase) && + c.Domain.Equals (domain, StringComparison.OrdinalIgnoreCase) && + c.Path.Equals (path, StringComparison.Ordinal)) + yield return c; + } + + #endregion + + #region Internal Methods + + internal void SendHeaders (bool closing, MemoryStream stream) + { + if (_contentType != null) { + var contentType = _contentEncoding != null && + _contentType.IndexOf ("charset=", StringComparison.Ordinal) == -1 + ? _contentType + "; charset=" + _contentEncoding.WebName + : _contentType; + + _headers.SetInternally ("Content-Type", contentType, true); + } + + if (_headers ["Server"] == null) + _headers.SetInternally ("Server", "websocket-sharp/1.0", true); + + var provider = CultureInfo.InvariantCulture; + if (_headers ["Date"] == null) + _headers.SetInternally ("Date", DateTime.UtcNow.ToString ("r", provider), true); + + if (!_chunked) { + if (!_contentLengthSet && closing) { + _contentLengthSet = true; + _contentLength = 0; + } + + if (_contentLengthSet) + _headers.SetInternally ("Content-Length", _contentLength.ToString (provider), true); + } + + var version = _context.Request.ProtocolVersion; + if (!_contentLengthSet && !_chunked && version >= HttpVersion.Version11) + _chunked = true; + + /* Apache forces closing the connection for these status codes: + * - HttpStatusCode.BadRequest 400 + * - HttpStatusCode.RequestTimeout 408 + * - HttpStatusCode.LengthRequired 411 + * - HttpStatusCode.RequestEntityTooLarge 413 + * - HttpStatusCode.RequestUriTooLong 414 + * - HttpStatusCode.InternalServerError 500 + * - HttpStatusCode.ServiceUnavailable 503 + */ + var connClose = _statusCode == 400 || + _statusCode == 408 || + _statusCode == 411 || + _statusCode == 413 || + _statusCode == 414 || + _statusCode == 500 || + _statusCode == 503; + + if (!connClose) + connClose = !_context.Request.KeepAlive; + + // They sent both KeepAlive: true and Connection: close!? + if (!_keepAlive || connClose) { + _headers.SetInternally ("Connection", "close", true); + connClose = true; + } + + if (_chunked) + _headers.SetInternally ("Transfer-Encoding", "chunked", true); + + int reuses = _context.Connection.Reuses; + if (reuses >= 100) { + _forceCloseChunked = true; + if (!connClose) { + _headers.SetInternally ("Connection", "close", true); + connClose = true; + } + } + + if (!connClose) { + _headers.SetInternally ( + "Keep-Alive", String.Format ("timeout=15,max={0}", 100 - reuses), true); + + if (_context.Request.ProtocolVersion <= HttpVersion.Version10) + _headers.SetInternally ("Connection", "keep-alive", true); + } + + if (_location != null) + _headers.SetInternally ("Location", _location, true); + + if (_cookies != null) + foreach (Cookie cookie in _cookies) + _headers.SetInternally ("Set-Cookie", cookie.ToResponseString (), true); + + var encoding = _contentEncoding ?? Encoding.Default; + var writer = new StreamWriter (stream, encoding, 256); + writer.Write ("HTTP/{0} {1} {2}\r\n", _version, _statusCode, _statusDescription); + + var headers = _headers.ToStringMultiValue (true); + writer.Write (headers); + writer.Flush (); + + var preamble = encoding.CodePage == 65001 ? 3 : encoding.GetPreamble ().Length; + if (_outputStream == null) + _outputStream = _context.Connection.GetResponseStream (); + + // Assumes that the stream was at position 0. + stream.Position = preamble; + _headersSent = true; + } + + #endregion + + #region Public Methods + + /// + /// Closes the connection to the client without sending a response. + /// + public void Abort () + { + if (_disposed) + return; + + close (true); + } + + /// + /// Adds an HTTP header with the specified and + /// to the headers for the response. + /// + /// + /// A that represents the name of the header to add. + /// + /// + /// A that represents the value of the header to add. + /// + /// + /// + /// or contains invalid characters. + /// + /// + /// -or- + /// + /// + /// is a restricted header name. + /// + /// + /// + /// is or empty. + /// + /// + /// The length of is greater than 65,535 characters. + /// + /// + /// + /// The response has already been sent. + /// + /// + /// -or- + /// + /// + /// The header cannot be allowed to add to the current headers. + /// + /// + /// + /// This object is closed. + /// + public void AddHeader (string name, string value) + { + checkDisposedOrHeadersSent (); + _headers.Set (name, value); + } + + /// + /// Appends the specified to the cookies sent with the response. + /// + /// + /// A to append. + /// + /// + /// is . + /// + /// + /// The response has already been sent. + /// + /// + /// This object is closed. + /// + public void AppendCookie (Cookie cookie) + { + checkDisposedOrHeadersSent (); + Cookies.Add (cookie); + } + + /// + /// Appends a to the specified HTTP header sent with the response. + /// + /// + /// A that represents the name of the header to append + /// to. + /// + /// + /// A that represents the value to append to the header. + /// + /// + /// + /// or contains invalid characters. + /// + /// + /// -or- + /// + /// + /// is a restricted header name. + /// + /// + /// + /// is or empty. + /// + /// + /// The length of is greater than 65,535 characters. + /// + /// + /// + /// The response has already been sent. + /// + /// + /// -or- + /// + /// + /// The current headers cannot allow the header to append a value. + /// + /// + /// + /// This object is closed. + /// + public void AppendHeader (string name, string value) + { + checkDisposedOrHeadersSent (); + _headers.Add (name, value); + } + + /// + /// Sends the response to the client, and releases the resources used by + /// this instance. + /// + public void Close () + { + if (_disposed) + return; + + close (false); + } + + /// + /// Sends the response with the specified array of to the client, and + /// releases the resources used by this instance. + /// + /// + /// An array of that contains the response entity body data. + /// + /// + /// true if this method blocks execution while flushing the stream to the client; + /// otherwise, false. + /// + /// + /// is . + /// + /// + /// The response has already been sent. + /// + /// + /// This object is closed. + /// + public void Close (byte [] responseEntity, bool willBlock) + { + if (responseEntity == null) + throw new ArgumentNullException ("responseEntity"); + + var len = responseEntity.Length; + ContentLength64 = len; + + var output = OutputStream; + if (willBlock) { + output.Write (responseEntity, 0, len); + close (false); + + return; + } + + output.BeginWrite ( + responseEntity, + 0, + len, + ar => { + output.EndWrite (ar); + close (false); + }, + null); + } + + /// + /// Copies properties from the specified to this response. + /// + /// + /// A to copy. + /// + /// + /// The response has already been sent. + /// + /// + /// This object is closed. + /// + public void CopyFrom (HttpListenerResponse templateResponse) + { + checkDisposedOrHeadersSent (); + + _headers.Clear (); + _headers.Add (templateResponse._headers); + _contentLength = templateResponse._contentLength; + _statusCode = templateResponse._statusCode; + _statusDescription = templateResponse._statusDescription; + _keepAlive = templateResponse._keepAlive; + _version = templateResponse._version; + } + + /// + /// Configures the response to redirect the client's request to the specified + /// . + /// + /// + /// A that represents the URL to redirect the client's request to. + /// + /// + /// The response has already been sent. + /// + /// + /// This object is closed. + /// + public void Redirect (string url) + { + StatusCode = (int) HttpStatusCode.Redirect; + _location = url; + } + + /// + /// Adds or updates a in the cookies sent with the response. + /// + /// + /// A to set. + /// + /// + /// already exists in the cookies, and couldn't be replaced. + /// + /// + /// is . + /// + /// + /// The response has already been sent. + /// + /// + /// This object is closed. + /// + public void SetCookie (Cookie cookie) + { + checkDisposedOrHeadersSent (); + if (cookie == null) + throw new ArgumentNullException ("cookie"); + + if (!canAddOrUpdate (cookie)) + throw new ArgumentException ("Cannot be replaced.", "cookie"); + + Cookies.Add (cookie); + } + + #endregion + + #region Explicit Interface Implementation + + /// + /// Releases all resource used by the . + /// + void IDisposable.Dispose () + { + if (_disposed) + return; + + // TODO: Abort or Close? + close (true); // Same as Abort. + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpStatusCode.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpStatusCode.cs new file mode 100644 index 0000000..86a0a5c --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpStatusCode.cs @@ -0,0 +1,359 @@ +#region License +/* + * HttpStatusCode.cs + * + * This code is derived from System.Net.HttpStatusCode.cs of Mono + * (http://www.mono-project.com). + * + * It was automatically generated from ECMA CLI XML Library Specification. + * Generator: libgen.xsl [1.0; (C) Sergey Chaban (serge@wildwestsoftware.com)] + * Created: Wed, 5 Sep 2001 06:32:05 UTC + * Source file: AllTypes.xml + * URL: http://msdn.microsoft.com/net/ecma/AllTypes.xml + * + * The MIT License + * + * Copyright (c) 2001 Ximian, Inc. (http://www.ximian.com) + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +namespace WebSocketSharp.Net +{ + /// + /// Contains the values of the HTTP status codes. + /// + /// + /// The HttpStatusCode enumeration contains the values of the HTTP status codes defined in + /// RFC 2616 for HTTP 1.1. + /// + public enum HttpStatusCode + { + /// + /// Equivalent to status code 100. + /// Indicates that the client should continue with its request. + /// + Continue = 100, + /// + /// Equivalent to status code 101. + /// Indicates that the server is switching the HTTP version or protocol on the connection. + /// + SwitchingProtocols = 101, + /// + /// Equivalent to status code 200. + /// Indicates that the client's request has succeeded. + /// + OK = 200, + /// + /// Equivalent to status code 201. + /// Indicates that the client's request has been fulfilled and resulted in a new resource being + /// created. + /// + Created = 201, + /// + /// Equivalent to status code 202. + /// Indicates that the client's request has been accepted for processing, but the processing + /// hasn't been completed. + /// + Accepted = 202, + /// + /// Equivalent to status code 203. + /// Indicates that the returned metainformation is from a local or a third-party copy instead of + /// the origin server. + /// + NonAuthoritativeInformation = 203, + /// + /// Equivalent to status code 204. + /// Indicates that the server has fulfilled the client's request but doesn't need to return + /// an entity-body. + /// + NoContent = 204, + /// + /// Equivalent to status code 205. + /// Indicates that the server has fulfilled the client's request, and the user agent should + /// reset the document view which caused the request to be sent. + /// + ResetContent = 205, + /// + /// Equivalent to status code 206. + /// Indicates that the server has fulfilled the partial GET request for the resource. + /// + PartialContent = 206, + /// + /// + /// Equivalent to status code 300. + /// Indicates that the requested resource corresponds to any of multiple representations. + /// + /// + /// MultipleChoices is a synonym for Ambiguous. + /// + /// + MultipleChoices = 300, + /// + /// + /// Equivalent to status code 300. + /// Indicates that the requested resource corresponds to any of multiple representations. + /// + /// + /// Ambiguous is a synonym for MultipleChoices. + /// + /// + Ambiguous = 300, + /// + /// + /// Equivalent to status code 301. + /// Indicates that the requested resource has been assigned a new permanent URI and + /// any future references to this resource should use one of the returned URIs. + /// + /// + /// MovedPermanently is a synonym for Moved. + /// + /// + MovedPermanently = 301, + /// + /// + /// Equivalent to status code 301. + /// Indicates that the requested resource has been assigned a new permanent URI and + /// any future references to this resource should use one of the returned URIs. + /// + /// + /// Moved is a synonym for MovedPermanently. + /// + /// + Moved = 301, + /// + /// + /// Equivalent to status code 302. + /// Indicates that the requested resource is located temporarily under a different URI. + /// + /// + /// Found is a synonym for Redirect. + /// + /// + Found = 302, + /// + /// + /// Equivalent to status code 302. + /// Indicates that the requested resource is located temporarily under a different URI. + /// + /// + /// Redirect is a synonym for Found. + /// + /// + Redirect = 302, + /// + /// + /// Equivalent to status code 303. + /// Indicates that the response to the request can be found under a different URI and + /// should be retrieved using a GET method on that resource. + /// + /// + /// SeeOther is a synonym for RedirectMethod. + /// + /// + SeeOther = 303, + /// + /// + /// Equivalent to status code 303. + /// Indicates that the response to the request can be found under a different URI and + /// should be retrieved using a GET method on that resource. + /// + /// + /// RedirectMethod is a synonym for SeeOther. + /// + /// + RedirectMethod = 303, + /// + /// Equivalent to status code 304. + /// Indicates that the client has performed a conditional GET request and access is allowed, + /// but the document hasn't been modified. + /// + NotModified = 304, + /// + /// Equivalent to status code 305. + /// Indicates that the requested resource must be accessed through the proxy given by + /// the Location field. + /// + UseProxy = 305, + /// + /// Equivalent to status code 306. + /// This status code was used in a previous version of the specification, is no longer used, + /// and is reserved for future use. + /// + Unused = 306, + /// + /// + /// Equivalent to status code 307. + /// Indicates that the requested resource is located temporarily under a different URI. + /// + /// + /// TemporaryRedirect is a synonym for RedirectKeepVerb. + /// + /// + TemporaryRedirect = 307, + /// + /// + /// Equivalent to status code 307. + /// Indicates that the requested resource is located temporarily under a different URI. + /// + /// + /// RedirectKeepVerb is a synonym for TemporaryRedirect. + /// + /// + RedirectKeepVerb = 307, + /// + /// Equivalent to status code 400. + /// Indicates that the client's request couldn't be understood by the server due to + /// malformed syntax. + /// + BadRequest = 400, + /// + /// Equivalent to status code 401. + /// Indicates that the client's request requires user authentication. + /// + Unauthorized = 401, + /// + /// Equivalent to status code 402. + /// This status code is reserved for future use. + /// + PaymentRequired = 402, + /// + /// Equivalent to status code 403. + /// Indicates that the server understood the client's request but is refusing to fulfill it. + /// + Forbidden = 403, + /// + /// Equivalent to status code 404. + /// Indicates that the server hasn't found anything matching the request URI. + /// + NotFound = 404, + /// + /// Equivalent to status code 405. + /// Indicates that the method specified in the request line isn't allowed for the resource + /// identified by the request URI. + /// + MethodNotAllowed = 405, + /// + /// Equivalent to status code 406. + /// Indicates that the server doesn't have the appropriate resource to respond to the Accept + /// headers in the client's request. + /// + NotAcceptable = 406, + /// + /// Equivalent to status code 407. + /// Indicates that the client must first authenticate itself with the proxy. + /// + ProxyAuthenticationRequired = 407, + /// + /// Equivalent to status code 408. + /// Indicates that the client didn't produce a request within the time that the server was + /// prepared to wait. + /// + RequestTimeout = 408, + /// + /// Equivalent to status code 409. + /// Indicates that the client's request couldn't be completed due to a conflict on the server. + /// + Conflict = 409, + /// + /// Equivalent to status code 410. + /// Indicates that the requested resource is no longer available at the server and + /// no forwarding address is known. + /// + Gone = 410, + /// + /// Equivalent to status code 411. + /// Indicates that the server refuses to accept the client's request without a defined + /// Content-Length. + /// + LengthRequired = 411, + /// + /// Equivalent to status code 412. + /// Indicates that the precondition given in one or more of the request headers evaluated to + /// false when it was tested on the server. + /// + PreconditionFailed = 412, + /// + /// Equivalent to status code 413. + /// Indicates that the entity of the client's request is larger than the server is willing or + /// able to process. + /// + RequestEntityTooLarge = 413, + /// + /// Equivalent to status code 414. + /// Indicates that the request URI is longer than the server is willing to interpret. + /// + RequestUriTooLong = 414, + /// + /// Equivalent to status code 415. + /// Indicates that the entity of the client's request is in a format not supported by + /// the requested resource for the requested method. + /// + UnsupportedMediaType = 415, + /// + /// Equivalent to status code 416. + /// Indicates that none of the range specifier values in a Range request header overlap + /// the current extent of the selected resource. + /// + RequestedRangeNotSatisfiable = 416, + /// + /// Equivalent to status code 417. + /// Indicates that the expectation given in an Expect request header couldn't be met by + /// the server. + /// + ExpectationFailed = 417, + /// + /// Equivalent to status code 500. + /// Indicates that the server encountered an unexpected condition which prevented it from + /// fulfilling the client's request. + /// + InternalServerError = 500, + /// + /// Equivalent to status code 501. + /// Indicates that the server doesn't support the functionality required to fulfill the client's + /// request. + /// + NotImplemented = 501, + /// + /// Equivalent to status code 502. + /// Indicates that a gateway or proxy server received an invalid response from the upstream + /// server. + /// + BadGateway = 502, + /// + /// Equivalent to status code 503. + /// Indicates that the server is currently unable to handle the client's request due to + /// a temporary overloading or maintenance of the server. + /// + ServiceUnavailable = 503, + /// + /// Equivalent to status code 504. + /// Indicates that a gateway or proxy server didn't receive a timely response from the upstream + /// server or some other auxiliary server. + /// + GatewayTimeout = 504, + /// + /// Equivalent to status code 505. + /// Indicates that the server doesn't support the HTTP version used in the client's request. + /// + HttpVersionNotSupported = 505, + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpStreamAsyncResult.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpStreamAsyncResult.cs new file mode 100644 index 0000000..f84a164 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpStreamAsyncResult.cs @@ -0,0 +1,133 @@ +#region License +/* + * HttpStreamAsyncResult.cs + * + * This code is derived from System.Net.HttpStreamAsyncResult.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; +using System.Threading; + +namespace WebSocketSharp.Net +{ + internal class HttpStreamAsyncResult : IAsyncResult + { + #region Private Fields + + private AsyncCallback _callback; + private bool _completed; + private object _state; + private object _sync; + private ManualResetEvent _waitHandle; + + #endregion + + #region Internal Fields + + internal byte [] Buffer; + internal int Count; + internal Exception Error; + internal int Offset; + internal int SyncRead; + + #endregion + + #region Public Constructors + + public HttpStreamAsyncResult (AsyncCallback callback, object state) + { + _callback = callback; + _state = state; + _sync = new object (); + } + + #endregion + + #region Public Properties + + public object AsyncState { + get { + return _state; + } + } + + public WaitHandle AsyncWaitHandle { + get { + lock (_sync) + return _waitHandle ?? (_waitHandle = new ManualResetEvent (_completed)); + } + } + + public bool CompletedSynchronously { + get { + return SyncRead == Count; + } + } + + public bool IsCompleted { + get { + lock (_sync) + return _completed; + } + } + + #endregion + + #region Public Methods + + public void Complete () + { + lock (_sync) { + if (_completed) + return; + + _completed = true; + if (_waitHandle != null) + _waitHandle.Set (); + + if (_callback != null) + _callback.BeginInvoke (this, ar => _callback.EndInvoke (ar), null); + } + } + + public void Complete (Exception exception) + { + Error = exception; + Complete (); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpUtility.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpUtility.cs new file mode 100644 index 0000000..ab0d031 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpUtility.cs @@ -0,0 +1,1204 @@ +#region License +/* + * HttpUtility.cs + * + * This code is derived from System.Net.HttpUtility.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005-2009 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Patrik Torstensson + * - Wictor Wilén (decode/encode functions) + * - Tim Coleman + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Globalization; +using System.IO; +using System.Text; + +namespace WebSocketSharp.Net +{ + internal sealed class HttpUtility + { + #region Private Static Fields + + private static Dictionary _entities; + private static char [] _hexChars = "0123456789abcdef".ToCharArray (); + private static object _sync = new object (); + + #endregion + + #region Private Static Properties + + private static Dictionary Entities { + get { + lock (_sync) { + if (_entities == null) + initEntities (); + + return _entities; + } + } + } + + #endregion + + #region Private Methods + + private static int getChar (byte [] bytes, int offset, int length) + { + var value = 0; + var end = length + offset; + for (int i = offset; i < end; i++) { + var current = getInt (bytes [i]); + if (current == -1) + return -1; + + value = (value << 4) + current; + } + + return value; + } + + private static int getChar (string s, int offset, int length) + { + var value = 0; + var end = length + offset; + for (int i = offset; i < end; i++) { + var c = s [i]; + if (c > 127) + return -1; + + var current = getInt ((byte) c); + if (current == -1) + return -1; + + value = (value << 4) + current; + } + + return value; + } + + private static char [] getChars (MemoryStream buffer, Encoding encoding) + { + return encoding.GetChars (buffer.GetBuffer (), 0, (int) buffer.Length); + } + + private static int getInt (byte b) + { + var c = (char) b; + return c >= '0' && c <= '9' + ? c - '0' + : c >= 'a' && c <= 'f' + ? c - 'a' + 10 + : c >= 'A' && c <= 'F' + ? c - 'A' + 10 + : -1; + } + + private static void initEntities () + { + // Build the dictionary of HTML entity references. + // This list comes from the HTML 4.01 W3C recommendation. + _entities = new Dictionary (); + _entities.Add ("nbsp", '\u00A0'); + _entities.Add ("iexcl", '\u00A1'); + _entities.Add ("cent", '\u00A2'); + _entities.Add ("pound", '\u00A3'); + _entities.Add ("curren", '\u00A4'); + _entities.Add ("yen", '\u00A5'); + _entities.Add ("brvbar", '\u00A6'); + _entities.Add ("sect", '\u00A7'); + _entities.Add ("uml", '\u00A8'); + _entities.Add ("copy", '\u00A9'); + _entities.Add ("ordf", '\u00AA'); + _entities.Add ("laquo", '\u00AB'); + _entities.Add ("not", '\u00AC'); + _entities.Add ("shy", '\u00AD'); + _entities.Add ("reg", '\u00AE'); + _entities.Add ("macr", '\u00AF'); + _entities.Add ("deg", '\u00B0'); + _entities.Add ("plusmn", '\u00B1'); + _entities.Add ("sup2", '\u00B2'); + _entities.Add ("sup3", '\u00B3'); + _entities.Add ("acute", '\u00B4'); + _entities.Add ("micro", '\u00B5'); + _entities.Add ("para", '\u00B6'); + _entities.Add ("middot", '\u00B7'); + _entities.Add ("cedil", '\u00B8'); + _entities.Add ("sup1", '\u00B9'); + _entities.Add ("ordm", '\u00BA'); + _entities.Add ("raquo", '\u00BB'); + _entities.Add ("frac14", '\u00BC'); + _entities.Add ("frac12", '\u00BD'); + _entities.Add ("frac34", '\u00BE'); + _entities.Add ("iquest", '\u00BF'); + _entities.Add ("Agrave", '\u00C0'); + _entities.Add ("Aacute", '\u00C1'); + _entities.Add ("Acirc", '\u00C2'); + _entities.Add ("Atilde", '\u00C3'); + _entities.Add ("Auml", '\u00C4'); + _entities.Add ("Aring", '\u00C5'); + _entities.Add ("AElig", '\u00C6'); + _entities.Add ("Ccedil", '\u00C7'); + _entities.Add ("Egrave", '\u00C8'); + _entities.Add ("Eacute", '\u00C9'); + _entities.Add ("Ecirc", '\u00CA'); + _entities.Add ("Euml", '\u00CB'); + _entities.Add ("Igrave", '\u00CC'); + _entities.Add ("Iacute", '\u00CD'); + _entities.Add ("Icirc", '\u00CE'); + _entities.Add ("Iuml", '\u00CF'); + _entities.Add ("ETH", '\u00D0'); + _entities.Add ("Ntilde", '\u00D1'); + _entities.Add ("Ograve", '\u00D2'); + _entities.Add ("Oacute", '\u00D3'); + _entities.Add ("Ocirc", '\u00D4'); + _entities.Add ("Otilde", '\u00D5'); + _entities.Add ("Ouml", '\u00D6'); + _entities.Add ("times", '\u00D7'); + _entities.Add ("Oslash", '\u00D8'); + _entities.Add ("Ugrave", '\u00D9'); + _entities.Add ("Uacute", '\u00DA'); + _entities.Add ("Ucirc", '\u00DB'); + _entities.Add ("Uuml", '\u00DC'); + _entities.Add ("Yacute", '\u00DD'); + _entities.Add ("THORN", '\u00DE'); + _entities.Add ("szlig", '\u00DF'); + _entities.Add ("agrave", '\u00E0'); + _entities.Add ("aacute", '\u00E1'); + _entities.Add ("acirc", '\u00E2'); + _entities.Add ("atilde", '\u00E3'); + _entities.Add ("auml", '\u00E4'); + _entities.Add ("aring", '\u00E5'); + _entities.Add ("aelig", '\u00E6'); + _entities.Add ("ccedil", '\u00E7'); + _entities.Add ("egrave", '\u00E8'); + _entities.Add ("eacute", '\u00E9'); + _entities.Add ("ecirc", '\u00EA'); + _entities.Add ("euml", '\u00EB'); + _entities.Add ("igrave", '\u00EC'); + _entities.Add ("iacute", '\u00ED'); + _entities.Add ("icirc", '\u00EE'); + _entities.Add ("iuml", '\u00EF'); + _entities.Add ("eth", '\u00F0'); + _entities.Add ("ntilde", '\u00F1'); + _entities.Add ("ograve", '\u00F2'); + _entities.Add ("oacute", '\u00F3'); + _entities.Add ("ocirc", '\u00F4'); + _entities.Add ("otilde", '\u00F5'); + _entities.Add ("ouml", '\u00F6'); + _entities.Add ("divide", '\u00F7'); + _entities.Add ("oslash", '\u00F8'); + _entities.Add ("ugrave", '\u00F9'); + _entities.Add ("uacute", '\u00FA'); + _entities.Add ("ucirc", '\u00FB'); + _entities.Add ("uuml", '\u00FC'); + _entities.Add ("yacute", '\u00FD'); + _entities.Add ("thorn", '\u00FE'); + _entities.Add ("yuml", '\u00FF'); + _entities.Add ("fnof", '\u0192'); + _entities.Add ("Alpha", '\u0391'); + _entities.Add ("Beta", '\u0392'); + _entities.Add ("Gamma", '\u0393'); + _entities.Add ("Delta", '\u0394'); + _entities.Add ("Epsilon", '\u0395'); + _entities.Add ("Zeta", '\u0396'); + _entities.Add ("Eta", '\u0397'); + _entities.Add ("Theta", '\u0398'); + _entities.Add ("Iota", '\u0399'); + _entities.Add ("Kappa", '\u039A'); + _entities.Add ("Lambda", '\u039B'); + _entities.Add ("Mu", '\u039C'); + _entities.Add ("Nu", '\u039D'); + _entities.Add ("Xi", '\u039E'); + _entities.Add ("Omicron", '\u039F'); + _entities.Add ("Pi", '\u03A0'); + _entities.Add ("Rho", '\u03A1'); + _entities.Add ("Sigma", '\u03A3'); + _entities.Add ("Tau", '\u03A4'); + _entities.Add ("Upsilon", '\u03A5'); + _entities.Add ("Phi", '\u03A6'); + _entities.Add ("Chi", '\u03A7'); + _entities.Add ("Psi", '\u03A8'); + _entities.Add ("Omega", '\u03A9'); + _entities.Add ("alpha", '\u03B1'); + _entities.Add ("beta", '\u03B2'); + _entities.Add ("gamma", '\u03B3'); + _entities.Add ("delta", '\u03B4'); + _entities.Add ("epsilon", '\u03B5'); + _entities.Add ("zeta", '\u03B6'); + _entities.Add ("eta", '\u03B7'); + _entities.Add ("theta", '\u03B8'); + _entities.Add ("iota", '\u03B9'); + _entities.Add ("kappa", '\u03BA'); + _entities.Add ("lambda", '\u03BB'); + _entities.Add ("mu", '\u03BC'); + _entities.Add ("nu", '\u03BD'); + _entities.Add ("xi", '\u03BE'); + _entities.Add ("omicron", '\u03BF'); + _entities.Add ("pi", '\u03C0'); + _entities.Add ("rho", '\u03C1'); + _entities.Add ("sigmaf", '\u03C2'); + _entities.Add ("sigma", '\u03C3'); + _entities.Add ("tau", '\u03C4'); + _entities.Add ("upsilon", '\u03C5'); + _entities.Add ("phi", '\u03C6'); + _entities.Add ("chi", '\u03C7'); + _entities.Add ("psi", '\u03C8'); + _entities.Add ("omega", '\u03C9'); + _entities.Add ("thetasym", '\u03D1'); + _entities.Add ("upsih", '\u03D2'); + _entities.Add ("piv", '\u03D6'); + _entities.Add ("bull", '\u2022'); + _entities.Add ("hellip", '\u2026'); + _entities.Add ("prime", '\u2032'); + _entities.Add ("Prime", '\u2033'); + _entities.Add ("oline", '\u203E'); + _entities.Add ("frasl", '\u2044'); + _entities.Add ("weierp", '\u2118'); + _entities.Add ("image", '\u2111'); + _entities.Add ("real", '\u211C'); + _entities.Add ("trade", '\u2122'); + _entities.Add ("alefsym", '\u2135'); + _entities.Add ("larr", '\u2190'); + _entities.Add ("uarr", '\u2191'); + _entities.Add ("rarr", '\u2192'); + _entities.Add ("darr", '\u2193'); + _entities.Add ("harr", '\u2194'); + _entities.Add ("crarr", '\u21B5'); + _entities.Add ("lArr", '\u21D0'); + _entities.Add ("uArr", '\u21D1'); + _entities.Add ("rArr", '\u21D2'); + _entities.Add ("dArr", '\u21D3'); + _entities.Add ("hArr", '\u21D4'); + _entities.Add ("forall", '\u2200'); + _entities.Add ("part", '\u2202'); + _entities.Add ("exist", '\u2203'); + _entities.Add ("empty", '\u2205'); + _entities.Add ("nabla", '\u2207'); + _entities.Add ("isin", '\u2208'); + _entities.Add ("notin", '\u2209'); + _entities.Add ("ni", '\u220B'); + _entities.Add ("prod", '\u220F'); + _entities.Add ("sum", '\u2211'); + _entities.Add ("minus", '\u2212'); + _entities.Add ("lowast", '\u2217'); + _entities.Add ("radic", '\u221A'); + _entities.Add ("prop", '\u221D'); + _entities.Add ("infin", '\u221E'); + _entities.Add ("ang", '\u2220'); + _entities.Add ("and", '\u2227'); + _entities.Add ("or", '\u2228'); + _entities.Add ("cap", '\u2229'); + _entities.Add ("cup", '\u222A'); + _entities.Add ("int", '\u222B'); + _entities.Add ("there4", '\u2234'); + _entities.Add ("sim", '\u223C'); + _entities.Add ("cong", '\u2245'); + _entities.Add ("asymp", '\u2248'); + _entities.Add ("ne", '\u2260'); + _entities.Add ("equiv", '\u2261'); + _entities.Add ("le", '\u2264'); + _entities.Add ("ge", '\u2265'); + _entities.Add ("sub", '\u2282'); + _entities.Add ("sup", '\u2283'); + _entities.Add ("nsub", '\u2284'); + _entities.Add ("sube", '\u2286'); + _entities.Add ("supe", '\u2287'); + _entities.Add ("oplus", '\u2295'); + _entities.Add ("otimes", '\u2297'); + _entities.Add ("perp", '\u22A5'); + _entities.Add ("sdot", '\u22C5'); + _entities.Add ("lceil", '\u2308'); + _entities.Add ("rceil", '\u2309'); + _entities.Add ("lfloor", '\u230A'); + _entities.Add ("rfloor", '\u230B'); + _entities.Add ("lang", '\u2329'); + _entities.Add ("rang", '\u232A'); + _entities.Add ("loz", '\u25CA'); + _entities.Add ("spades", '\u2660'); + _entities.Add ("clubs", '\u2663'); + _entities.Add ("hearts", '\u2665'); + _entities.Add ("diams", '\u2666'); + _entities.Add ("quot", '\u0022'); + _entities.Add ("amp", '\u0026'); + _entities.Add ("lt", '\u003C'); + _entities.Add ("gt", '\u003E'); + _entities.Add ("OElig", '\u0152'); + _entities.Add ("oelig", '\u0153'); + _entities.Add ("Scaron", '\u0160'); + _entities.Add ("scaron", '\u0161'); + _entities.Add ("Yuml", '\u0178'); + _entities.Add ("circ", '\u02C6'); + _entities.Add ("tilde", '\u02DC'); + _entities.Add ("ensp", '\u2002'); + _entities.Add ("emsp", '\u2003'); + _entities.Add ("thinsp", '\u2009'); + _entities.Add ("zwnj", '\u200C'); + _entities.Add ("zwj", '\u200D'); + _entities.Add ("lrm", '\u200E'); + _entities.Add ("rlm", '\u200F'); + _entities.Add ("ndash", '\u2013'); + _entities.Add ("mdash", '\u2014'); + _entities.Add ("lsquo", '\u2018'); + _entities.Add ("rsquo", '\u2019'); + _entities.Add ("sbquo", '\u201A'); + _entities.Add ("ldquo", '\u201C'); + _entities.Add ("rdquo", '\u201D'); + _entities.Add ("bdquo", '\u201E'); + _entities.Add ("dagger", '\u2020'); + _entities.Add ("Dagger", '\u2021'); + _entities.Add ("permil", '\u2030'); + _entities.Add ("lsaquo", '\u2039'); + _entities.Add ("rsaquo", '\u203A'); + _entities.Add ("euro", '\u20AC'); + } + + private static bool notEncoded (char c) + { + return c == '!' || + c == '\'' || + c == '(' || + c == ')' || + c == '*' || + c == '-' || + c == '.' || + c == '_'; + } + + private static void urlEncodeChar (char c, Stream result, bool isUnicode) + { + if (c > 255) { + // FIXME: What happens when there is an internal error? + //if (!isUnicode) + // throw new ArgumentOutOfRangeException ("c", c, "c must be less than 256."); + + result.WriteByte ((byte) '%'); + result.WriteByte ((byte) 'u'); + + var i = (int) c; + var idx = i >> 12; + result.WriteByte ((byte) _hexChars [idx]); + + idx = (i >> 8) & 0x0F; + result.WriteByte ((byte) _hexChars [idx]); + + idx = (i >> 4) & 0x0F; + result.WriteByte ((byte) _hexChars [idx]); + + idx = i & 0x0F; + result.WriteByte ((byte) _hexChars [idx]); + + return; + } + + if (c > ' ' && notEncoded (c)) { + result.WriteByte ((byte) c); + return; + } + + if (c == ' ') { + result.WriteByte ((byte) '+'); + return; + } + + if ((c < '0') || + (c < 'A' && c > '9') || + (c > 'Z' && c < 'a') || + (c > 'z')) { + if (isUnicode && c > 127) { + result.WriteByte ((byte) '%'); + result.WriteByte ((byte) 'u'); + result.WriteByte ((byte) '0'); + result.WriteByte ((byte) '0'); + } + else { + result.WriteByte ((byte) '%'); + } + + var idx = ((int) c) >> 4; + result.WriteByte ((byte) _hexChars [idx]); + + idx = ((int) c) & 0x0F; + result.WriteByte ((byte) _hexChars [idx]); + } + else { + result.WriteByte ((byte) c); + } + } + + private static void urlPathEncodeChar (char c, Stream result) + { + if (c < 33 || c > 126) { + var bytes = Encoding.UTF8.GetBytes (c.ToString ()); + foreach (var b in bytes) { + result.WriteByte ((byte) '%'); + + var i = ((int) b) >> 4; + result.WriteByte ((byte) _hexChars [i]); + + i = ((int) b) & 0x0F; + result.WriteByte ((byte) _hexChars [i]); + } + } + else if (c == ' ') { + result.WriteByte ((byte) '%'); + result.WriteByte ((byte) '2'); + result.WriteByte ((byte) '0'); + } + else { + result.WriteByte ((byte) c); + } + } + + private static void writeCharBytes (IList buffer, char c, Encoding encoding) + { + if (c > 255) + foreach (var b in encoding.GetBytes (new [] { c })) + buffer.Add (b); + else + buffer.Add ((byte) c); + } + + #endregion + + #region Internal Methods + + internal static Uri CreateRequestUrl ( + string requestUri, string host, bool websocketRequest, bool secure) + { + if (requestUri == null || requestUri.Length == 0 || host == null || host.Length == 0) + return null; + + string scheme = null; + string path = null; + if (requestUri.StartsWith ("/")) { + path = requestUri; + } + else if (requestUri.MaybeUri ()) { + Uri uri; + var valid = Uri.TryCreate (requestUri, UriKind.Absolute, out uri) && + (((scheme = uri.Scheme).StartsWith ("http") && !websocketRequest) || + (scheme.StartsWith ("ws") && websocketRequest)); + + if (!valid) + return null; + + host = uri.Authority; + path = uri.PathAndQuery; + } + else if (requestUri == "*") { + } + else { + // As authority form + host = requestUri; + } + + if (scheme == null) + scheme = (websocketRequest ? "ws" : "http") + (secure ? "s" : String.Empty); + + var colon = host.IndexOf (':'); + if (colon == -1) + host = String.Format ("{0}:{1}", host, scheme == "http" || scheme == "ws" ? 80 : 443); + + var url = String.Format ("{0}://{1}{2}", scheme, host, path); + + Uri res; + if (!Uri.TryCreate (url, UriKind.Absolute, out res)) + return null; + + return res; + } + + internal static NameValueCollection ParseQueryStringInternally (string query, Encoding encoding) + { + int len; + if (query == null || (len = query.Length) == 0 || (len == 1 && query [0] == '?')) + return new NameValueCollection (1); + + if (query [0] == '?') + query = query.Substring (1); + + var res = new QueryStringCollection (); + var components = query.Split ('&'); + foreach (var component in components) { + var i = component.IndexOf ('='); + if (i > -1) { + var name = UrlDecode (component.Substring (0, i), encoding); + var val = component.Length > i + 1 + ? UrlDecode (component.Substring (i + 1), encoding) + : String.Empty; + + res.Add (name, val); + } + else { + res.Add (null, UrlDecode (component, encoding)); + } + } + + return res; + } + + internal static string UrlDecodeInternally ( + byte [] bytes, int offset, int count, Encoding encoding) + { + var output = new StringBuilder (); + using (var acc = new MemoryStream ()) { + var end = count + offset; + + int xchar; + for (int i = offset; i < end; i++) { + if (bytes [i] == '%' && i + 2 < count && bytes [i + 1] != '%') { + if (bytes [i + 1] == (byte) 'u' && i + 5 < end) { + if (acc.Length > 0) { + output.Append (getChars (acc, encoding)); + acc.SetLength (0); + } + + xchar = getChar (bytes, i + 2, 4); + if (xchar != -1) { + output.Append ((char) xchar); + i += 5; + + continue; + } + } + else if ((xchar = getChar (bytes, i + 1, 2)) != -1) { + acc.WriteByte ((byte) xchar); + i += 2; + + continue; + } + } + + if (acc.Length > 0) { + output.Append (getChars (acc, encoding)); + acc.SetLength (0); + } + + if (bytes [i] == '+') + output.Append (' '); + else + output.Append ((char) bytes [i]); + } + + if (acc.Length > 0) + output.Append (getChars (acc, encoding)); + } + + return output.ToString (); + } + + internal static byte [] UrlDecodeToBytesInternally (byte [] bytes, int offset, int count) + { + using (var res = new MemoryStream ()) { + var end = offset + count; + for (int i = offset; i < end; i++) { + var c = (char) bytes [i]; + if (c == '+') { + c = ' '; + } + else if (c == '%' && i < end - 2) { + var xchar = getChar (bytes, i + 1, 2); + if (xchar != -1) { + c = (char) xchar; + i += 2; + } + } + + res.WriteByte ((byte) c); + } + + res.Close (); + return res.ToArray (); + } + } + + internal static byte [] UrlEncodeToBytesInternally (byte [] bytes, int offset, int count) + { + using (var res = new MemoryStream ()) { + var end = offset + count; + for (int i = offset; i < end; i++) + urlEncodeChar ((char) bytes [i], res, false); + + res.Close (); + return res.ToArray (); + } + } + + internal static byte [] UrlEncodeUnicodeToBytesInternally (string s) + { + using (var res = new MemoryStream ()) { + foreach (var c in s) + urlEncodeChar (c, res, true); + + res.Close (); + return res.ToArray (); + } + } + + #endregion + + #region Public Methods + + public static string HtmlAttributeEncode (string s) + { + if (s == null || s.Length == 0 || !s.Contains ('&', '"', '<', '>')) + return s; + + var output = new StringBuilder (); + foreach (var c in s) + output.Append ( + c == '&' + ? "&" + : c == '"' + ? """ + : c == '<' + ? "<" + : c == '>' + ? ">" + : c.ToString () + ); + + return output.ToString (); + } + + public static void HtmlAttributeEncode (string s, TextWriter output) + { + if (output == null) + throw new ArgumentNullException ("output"); + + output.Write (HtmlAttributeEncode (s)); + } + + /// + /// Decodes an HTML-encoded and returns the decoded . + /// + /// + /// A that represents the decoded string. + /// + /// + /// A to decode. + /// + public static string HtmlDecode (string s) + { + if (s == null || s.Length == 0 || !s.Contains ('&')) + return s; + + var entity = new StringBuilder (); + var output = new StringBuilder (); + + // 0 -> nothing, + // 1 -> right after '&' + // 2 -> between '&' and ';' but no '#' + // 3 -> '#' found after '&' and getting numbers + var state = 0; + var number = 0; + var haveTrailingDigits = false; + foreach (var c in s) { + if (state == 0) { + if (c == '&') { + entity.Append (c); + state = 1; + } + else { + output.Append (c); + } + + continue; + } + + if (c == '&') { + state = 1; + if (haveTrailingDigits) { + entity.Append (number.ToString (CultureInfo.InvariantCulture)); + haveTrailingDigits = false; + } + + output.Append (entity.ToString ()); + entity.Length = 0; + entity.Append ('&'); + + continue; + } + + if (state == 1) { + if (c == ';') { + state = 0; + output.Append (entity.ToString ()); + output.Append (c); + entity.Length = 0; + } + else { + number = 0; + if (c != '#') + state = 2; + else + state = 3; + + entity.Append (c); + } + } + else if (state == 2) { + entity.Append (c); + if (c == ';') { + var key = entity.ToString (); + if (key.Length > 1 && Entities.ContainsKey (key.Substring (1, key.Length - 2))) + key = Entities [key.Substring (1, key.Length - 2)].ToString (); + + output.Append (key); + state = 0; + entity.Length = 0; + } + } + else if (state == 3) { + if (c == ';') { + if (number > 65535) { + output.Append ("&#"); + output.Append (number.ToString (CultureInfo.InvariantCulture)); + output.Append (";"); + } + else { + output.Append ((char) number); + } + + state = 0; + entity.Length = 0; + haveTrailingDigits = false; + } + else if (Char.IsDigit (c)) { + number = number * 10 + ((int) c - '0'); + haveTrailingDigits = true; + } + else { + state = 2; + if (haveTrailingDigits) { + entity.Append (number.ToString (CultureInfo.InvariantCulture)); + haveTrailingDigits = false; + } + + entity.Append (c); + } + } + } + + if (entity.Length > 0) + output.Append (entity.ToString ()); + else if (haveTrailingDigits) + output.Append (number.ToString (CultureInfo.InvariantCulture)); + + return output.ToString (); + } + + /// + /// Decodes an HTML-encoded and sends the decoded + /// to the specified . + /// + /// + /// A to decode. + /// + /// + /// A that receives the decoded string. + /// + public static void HtmlDecode (string s, TextWriter output) + { + if (output == null) + throw new ArgumentNullException ("output"); + + output.Write (HtmlDecode (s)); + } + + /// + /// HTML-encodes a and returns the encoded . + /// + /// + /// A that represents the encoded string. + /// + /// + /// A to encode. + /// + public static string HtmlEncode (string s) + { + if (s == null || s.Length == 0) + return s; + + var needEncode = false; + foreach (var c in s) { + if (c == '&' || c == '"' || c == '<' || c == '>' || c > 159) { + needEncode = true; + break; + } + } + + if (!needEncode) + return s; + + var output = new StringBuilder (); + foreach (var c in s) { + if (c == '&') { + output.Append ("&"); + } + else if (c == '"') { + output.Append ("""); + } + else if (c == '<') { + output.Append ("<"); + } + else if (c == '>') { + output.Append (">"); + } + else { + // MS starts encoding with &# from 160 and stops at 255. + // We don't do that. One reason is the 65308/65310 unicode + // characters that look like '<' and '>'. + if (c > 159) { + output.Append ("&#"); + output.Append (((int) c).ToString (CultureInfo.InvariantCulture)); + output.Append (";"); + } + else { + output.Append (c); + } + } + } + + return output.ToString (); + } + + /// + /// HTML-encodes a and sends the encoded + /// to the specified . + /// + /// + /// A to encode. + /// + /// + /// A that receives the encoded string. + /// + public static void HtmlEncode (string s, TextWriter output) + { + if (output == null) + throw new ArgumentNullException ("output"); + + output.Write (HtmlEncode (s)); + } + + public static NameValueCollection ParseQueryString (string query) + { + if (query == null) + throw new ArgumentNullException ("query"); + + return ParseQueryStringInternally (query, Encoding.UTF8); + } + + public static NameValueCollection ParseQueryString (string query, Encoding encoding) + { + if (query == null) + throw new ArgumentNullException ("query"); + + if (encoding == null) + throw new ArgumentNullException ("encoding"); + + return ParseQueryStringInternally (query, encoding); + } + + public static string UrlDecode (string s) + { + return UrlDecode (s, Encoding.UTF8); + } + + public static string UrlDecode (string s, Encoding encoding) + { + if (s == null || s.Length == 0 || !s.Contains ('%', '+')) + return s; + + if (encoding == null) + encoding = Encoding.UTF8; + + var len = s.Length; + var buff = new List (); + + int xchar; + for (int i = 0; i < len; i++) { + var c = s [i]; + if (c == '%' && i + 2 < len && s [i + 1] != '%') { + if (s [i + 1] == 'u' && i + 5 < len) { + // Unicode hex sequence. + xchar = getChar (s, i + 2, 4); + if (xchar != -1) { + writeCharBytes (buff, (char) xchar, encoding); + i += 5; + } + else { + writeCharBytes (buff, '%', encoding); + } + } + else if ((xchar = getChar (s, i + 1, 2)) != -1) { + writeCharBytes (buff, (char) xchar, encoding); + i += 2; + } + else { + writeCharBytes (buff, '%', encoding); + } + + continue; + } + + if (c == '+') + writeCharBytes (buff, ' ', encoding); + else + writeCharBytes (buff, c, encoding); + } + + return encoding.GetString (buff.ToArray ()); + } + + public static string UrlDecode (byte [] bytes, Encoding encoding) + { + if (bytes == null) + return null; + + var len = bytes.Length; + if (len == 0) + return String.Empty; + + if (encoding == null) + encoding = Encoding.UTF8; + + return UrlDecodeInternally (bytes, 0, len, encoding); + } + + public static string UrlDecode (byte [] bytes, int offset, int count, Encoding encoding) + { + if (bytes == null) + return null; + + var len = bytes.Length; + if (len == 0 || count == 0) + return String.Empty; + + if (offset < 0 || offset >= len) + throw new ArgumentOutOfRangeException ("offset"); + + if (count < 0 || count > len - offset) + throw new ArgumentOutOfRangeException ("count"); + + if (encoding == null) + encoding = Encoding.UTF8; + + return UrlDecodeInternally (bytes, offset, count, encoding); + } + + public static byte [] UrlDecodeToBytes (byte [] bytes) + { + int len; + return bytes != null && (len = bytes.Length) > 0 + ? UrlDecodeToBytesInternally (bytes, 0, len) + : bytes; + } + + public static byte [] UrlDecodeToBytes (string s) + { + return UrlDecodeToBytes (s, Encoding.UTF8); + } + + public static byte [] UrlDecodeToBytes (string s, Encoding encoding) + { + if (s == null) + return null; + + if (s.Length == 0) + return new byte [0]; + + if (encoding == null) + encoding = Encoding.UTF8; + + var bytes = encoding.GetBytes (s); + return UrlDecodeToBytesInternally (bytes, 0, bytes.Length); + } + + public static byte [] UrlDecodeToBytes (byte [] bytes, int offset, int count) + { + int len; + if (bytes == null || (len = bytes.Length) == 0) + return bytes; + + if (count == 0) + return new byte [0]; + + if (offset < 0 || offset >= len) + throw new ArgumentOutOfRangeException ("offset"); + + if (count < 0 || count > len - offset ) + throw new ArgumentOutOfRangeException ("count"); + + return UrlDecodeToBytesInternally (bytes, offset, count); + } + + public static string UrlEncode (byte [] bytes) + { + int len; + return bytes == null + ? null + : (len = bytes.Length) == 0 + ? String.Empty + : Encoding.ASCII.GetString (UrlEncodeToBytesInternally (bytes, 0, len)); + } + + public static string UrlEncode (string s) + { + return UrlEncode (s, Encoding.UTF8); + } + + public static string UrlEncode (string s, Encoding encoding) + { + int len; + if (s == null || (len = s.Length) == 0) + return s; + + var needEncode = false; + foreach (var c in s) { + if ((c < '0') || (c < 'A' && c > '9') || (c > 'Z' && c < 'a') || (c > 'z')) { + if (notEncoded (c)) + continue; + + needEncode = true; + break; + } + } + + if (!needEncode) + return s; + + if (encoding == null) + encoding = Encoding.UTF8; + + // Avoided GetByteCount call. + var bytes = new byte [encoding.GetMaxByteCount (len)]; + var realLen = encoding.GetBytes (s, 0, len, bytes, 0); + + return Encoding.ASCII.GetString (UrlEncodeToBytesInternally (bytes, 0, realLen)); + } + + public static string UrlEncode (byte [] bytes, int offset, int count) + { + var encoded = UrlEncodeToBytes (bytes, offset, count); + return encoded == null + ? null + : encoded.Length == 0 + ? String.Empty + : Encoding.ASCII.GetString (encoded); + } + + public static byte [] UrlEncodeToBytes (byte [] bytes) + { + int len; + return bytes != null && (len = bytes.Length) > 0 + ? UrlEncodeToBytesInternally (bytes, 0, len) + : bytes; + } + + public static byte [] UrlEncodeToBytes (string s) + { + return UrlEncodeToBytes (s, Encoding.UTF8); + } + + public static byte [] UrlEncodeToBytes (string s, Encoding encoding) + { + if (s == null) + return null; + + if (s.Length == 0) + return new byte [0]; + + if (encoding == null) + encoding = Encoding.UTF8; + + var bytes = encoding.GetBytes (s); + return UrlEncodeToBytesInternally (bytes, 0, bytes.Length); + } + + public static byte [] UrlEncodeToBytes (byte [] bytes, int offset, int count) + { + int len; + if (bytes == null || (len = bytes.Length) == 0) + return bytes; + + if (count == 0) + return new byte [0]; + + if (offset < 0 || offset >= len) + throw new ArgumentOutOfRangeException ("offset"); + + if (count < 0 || count > len - offset) + throw new ArgumentOutOfRangeException ("count"); + + return UrlEncodeToBytesInternally (bytes, offset, count); + } + + public static string UrlEncodeUnicode (string s) + { + return s != null && s.Length > 0 + ? Encoding.ASCII.GetString (UrlEncodeUnicodeToBytesInternally (s)) + : s; + } + + public static byte [] UrlEncodeUnicodeToBytes (string s) + { + return s == null + ? null + : s.Length == 0 + ? new byte [0] + : UrlEncodeUnicodeToBytesInternally (s); + } + + public static string UrlPathEncode (string s) + { + if (s == null || s.Length == 0) + return s; + + using (var res = new MemoryStream ()) { + foreach (var c in s) + urlPathEncodeChar (c, res); + + res.Close (); + return Encoding.ASCII.GetString (res.ToArray ()); + } + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpVersion.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpVersion.cs new file mode 100644 index 0000000..81fbe10 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/HttpVersion.cs @@ -0,0 +1,73 @@ +#region License +/* + * HttpVersion.cs + * + * This code is derived from System.Net.HttpVersion.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Lawrence Pit + */ +#endregion + +using System; + +namespace WebSocketSharp.Net +{ + /// + /// Provides the HTTP version numbers. + /// + public class HttpVersion + { + #region Public Static Fields + + /// + /// Provides a instance for HTTP 1.0. + /// + public static readonly Version Version10 = new Version (1, 0); + + /// + /// Provides a instance for HTTP 1.1. + /// + public static readonly Version Version11 = new Version (1, 1); + + #endregion + + #region Public Constructors + + /// + /// Initializes a new instance of the class. + /// + public HttpVersion () + { + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/InputChunkState.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/InputChunkState.cs new file mode 100644 index 0000000..04f84df --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/InputChunkState.cs @@ -0,0 +1,51 @@ +#region License +/* + * InputChunkState.cs + * + * This code is derived from System.Net.ChunkStream.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2003 Ximian, Inc (http://www.ximian.com) + * Copyright (c) 2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; + +namespace WebSocketSharp.Net +{ + internal enum InputChunkState + { + None, + Body, + BodyFinished, + Trailer + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/InputState.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/InputState.cs new file mode 100644 index 0000000..72361df --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/InputState.cs @@ -0,0 +1,49 @@ +#region License +/* + * InputState.cs + * + * This code is derived from System.Net.HttpConnection.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; + +namespace WebSocketSharp.Net +{ + internal enum InputState + { + RequestLine, + Headers + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/LineState.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/LineState.cs new file mode 100644 index 0000000..7266c7e --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/LineState.cs @@ -0,0 +1,50 @@ +#region License +/* + * LineState.cs + * + * This code is derived from System.Net.HttpConnection.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; + +namespace WebSocketSharp.Net +{ + internal enum LineState + { + None, + CR, + LF + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/ListenerAsyncResult.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/ListenerAsyncResult.cs new file mode 100644 index 0000000..5910dea --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/ListenerAsyncResult.cs @@ -0,0 +1,198 @@ +#region License +/* + * ListenerAsyncResult.cs + * + * This code is derived from System.Net.ListenerAsyncResult.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005 Ximian, Inc. (http://www.ximian.com) + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; +using System.Threading; + +namespace WebSocketSharp.Net +{ + internal class ListenerAsyncResult : IAsyncResult + { + #region Private Fields + + private AsyncCallback _callback; + private bool _completed; + private HttpListenerContext _context; + private Exception _exception; + private ManualResetEvent _waitHandle; + private object _state; + private object _sync; + private bool _syncCompleted; + + #endregion + + #region Internal Fields + + internal bool EndCalled; + internal bool InGet; + + #endregion + + #region Public Constructors + + public ListenerAsyncResult (AsyncCallback callback, object state) + { + _callback = callback; + _state = state; + _sync = new object (); + } + + #endregion + + #region Public Properties + + public object AsyncState { + get { + return _state; + } + } + + public WaitHandle AsyncWaitHandle { + get { + lock (_sync) + return _waitHandle ?? (_waitHandle = new ManualResetEvent (_completed)); + } + } + + public bool CompletedSynchronously { + get { + return _syncCompleted; + } + } + + public bool IsCompleted { + get { + lock (_sync) + return _completed; + } + } + + #endregion + + #region Private Methods + + private static void invokeCallback (object state) + { + try { + var ares = (ListenerAsyncResult) state; + ares._callback (ares); + } + catch { + } + } + + #endregion + + #region Internal Methods + + internal void Complete (Exception exception) + { + _exception = InGet && (exception is ObjectDisposedException) + ? new HttpListenerException (500, "Listener closed.") + : exception; + + lock (_sync) { + _completed = true; + if (_waitHandle != null) + _waitHandle.Set (); + + if (_callback != null) + ThreadPool.QueueUserWorkItem(invokeCallback, this); + } + } + + internal void Complete (HttpListenerContext context) + { + Complete (context, false); + } + + internal void Complete (HttpListenerContext context, bool syncCompleted) + { + var listener = context.Listener; + var scheme = listener.SelectAuthenticationScheme (context); + if (scheme == AuthenticationSchemes.None) { + context.Response.Close (HttpStatusCode.Forbidden); + listener.BeginGetContext (this); + + return; + } + + var header = context.Request.Headers ["Authorization"]; + if (scheme == AuthenticationSchemes.Basic && + (header == null || !header.StartsWith ("basic", StringComparison.OrdinalIgnoreCase))) { + context.Response.CloseWithAuthChallenge ( + AuthenticationChallenge.CreateBasicChallenge (listener.Realm).ToBasicString ()); + + listener.BeginGetContext (this); + return; + } + + if (scheme == AuthenticationSchemes.Digest && + (header == null || !header.StartsWith ("digest", StringComparison.OrdinalIgnoreCase))) { + context.Response.CloseWithAuthChallenge ( + AuthenticationChallenge.CreateDigestChallenge (listener.Realm).ToDigestString ()); + + listener.BeginGetContext (this); + return; + } + + _context = context; + _syncCompleted = syncCompleted; + + lock (_sync) { + _completed = true; + if (_waitHandle != null) + _waitHandle.Set (); + + if (_callback != null) + ThreadPool.QueueUserWorkItem(invokeCallback, this); + } + } + + internal HttpListenerContext GetContext () + { + if (_exception != null) + throw _exception; + + return _context; + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/ListenerPrefix.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/ListenerPrefix.cs new file mode 100644 index 0000000..1ead612 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/ListenerPrefix.cs @@ -0,0 +1,206 @@ +#region License +/* + * ListenerPrefix.cs + * + * This code is derived from System.Net.ListenerPrefix.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Gonzalo Paniagua Javier + * - Oleg Mihailik + */ +#endregion + +using System; +using System.Net; + +namespace WebSocketSharp.Net +{ + internal sealed class ListenerPrefix + { + #region Private Fields + + IPAddress [] _addresses; + string _host; + string _original; + string _path; + ushort _port; + bool _secure; + + #endregion + + #region Public Fields + + public HttpListener Listener; + + #endregion + + #region Public Constructors + + // Must be called after calling ListenerPrefix.CheckUriPrefix. + public ListenerPrefix (string uriPrefix) + { + _original = uriPrefix; + parse (uriPrefix); + } + + #endregion + + #region Public Properties + + public IPAddress [] Addresses { + get { + return _addresses; + } + + set { + _addresses = value; + } + } + + public string Host { + get { + return _host; + } + } + + public string Path { + get { + return _path; + } + } + + public int Port { + get { + return (int) _port; + } + } + + public bool Secure { + get { + return _secure; + } + } + + #endregion + + #region Private Methods + + private void parse (string uriPrefix) + { + var defaultPort = uriPrefix.StartsWith ("https://") ? 443 : 80; + if (defaultPort == 443) + _secure = true; + + var length = uriPrefix.Length; + var startHost = uriPrefix.IndexOf (':') + 3; + var colon = uriPrefix.IndexOf (':', startHost, length - startHost); + int root; + if (colon > 0) { + root = uriPrefix.IndexOf ('/', colon, length - colon); + _host = uriPrefix.Substring (startHost, colon - startHost); + _port = (ushort) Int32.Parse (uriPrefix.Substring (colon + 1, root - colon - 1)); + _path = uriPrefix.Substring (root); + } + else { + root = uriPrefix.IndexOf ('/', startHost, length - startHost); + _host = uriPrefix.Substring (startHost, root - startHost); + _port = (ushort) defaultPort; + _path = uriPrefix.Substring (root); + } + + if (_path.Length > 1) + _path = _path.Substring (0, _path.Length - 1); + } + + #endregion + + #region public Methods + + public static void CheckUriPrefix (string uriPrefix) + { + if (uriPrefix == null) + throw new ArgumentNullException ("uriPrefix"); + + if (!uriPrefix.StartsWith ("http://") && !uriPrefix.StartsWith ("https://")) + throw new ArgumentException ("Only 'http' and 'https' schemes are supported."); + + var length = uriPrefix.Length; + var startHost = uriPrefix.IndexOf (':') + 3; + if (startHost >= length) + throw new ArgumentException ("No host specified."); + + var colon = uriPrefix.IndexOf (':', startHost, length - startHost); + if (startHost == colon) + throw new ArgumentException ("No host specified."); + + int root; + if (colon > 0) { + root = uriPrefix.IndexOf ('/', colon, length - colon); + if (root == -1) + throw new ArgumentException ("No path specified."); + + int port; + if (!Int32.TryParse (uriPrefix.Substring (colon + 1, root - colon - 1), out port) || + (port <= 0 || port >= 65536)) + throw new ArgumentException ("Invalid port."); + } + else { + root = uriPrefix.IndexOf ('/', startHost, length - startHost); + if (root == -1) + throw new ArgumentException ("No path specified."); + } + + if (uriPrefix [uriPrefix.Length - 1] != '/') + throw new ArgumentException ("The URI prefix must end with '/'."); + } + + // Equals and GetHashCode are required to detect duplicates in HttpListenerPrefixCollection. + public override bool Equals (object obj) + { + var other = obj as ListenerPrefix; + return other != null + ? _original == other._original + : false; + } + + public override int GetHashCode () + { + return _original.GetHashCode (); + } + + public override string ToString () + { + return _original; + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/NetworkCredential.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/NetworkCredential.cs new file mode 100644 index 0000000..e66394a --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/NetworkCredential.cs @@ -0,0 +1,179 @@ +#region License +/* + * NetworkCredential.cs + * + * The MIT License + * + * Copyright (c) 2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; + +namespace WebSocketSharp.Net +{ + /// + /// Provides the credentials for HTTP authentication (Basic/Digest). + /// + public class NetworkCredential + { + #region Private Fields + + private string _domain; + private string _password; + private string [] _roles; + private string _username; + + #endregion + + #region Public Constructors + + /// + /// Initializes a new instance of the class + /// with the specified user name and password. + /// + /// + /// A that represents the user name associated with the + /// credentials. + /// + /// + /// A that represents the password for the user name + /// associated with the credentials. + /// + /// + /// is or empty. + /// + public NetworkCredential (string username, string password) + : this (username, password, null, new string [0]) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified user name, password, domain, and roles. + /// + /// + /// A that represents the user name associated with the + /// credentials. + /// + /// + /// A that represents the password for the user name + /// associated with the credentials. + /// + /// + /// A that represents the name of the user domain + /// associated with the credentials. + /// + /// + /// An array of that contains the role names to which + /// the user associated with the credentials belongs if any. + /// + /// + /// is or empty. + /// + public NetworkCredential ( + string username, string password, string domain, params string [] roles) + { + if (username == null || username.Length == 0) + throw new ArgumentException ("Must not be null or empty.", "username"); + + _username = username; + _password = password; + _domain = domain; + _roles = roles; + } + + #endregion + + #region Public Properties + + /// + /// Gets the name of the user domain associated with the credentials. + /// + /// + /// A that represents the name of the user domain + /// associated with the credentials. + /// + public string Domain { + get { + return _domain ?? String.Empty; + } + + internal set { + _domain = value; + } + } + + /// + /// Gets the password for the user name associated with the credentials. + /// + /// + /// A that represents the password for the user name + /// associated with the credentials. + /// + public string Password { + get { + return _password ?? String.Empty; + } + + internal set { + _password = value; + } + } + + /// + /// Gets the role names to which the user associated with the credentials + /// belongs. + /// + /// + /// An array of that contains the role names to which + /// the user associated with the credentials belongs. + /// + public string [] Roles { + get { + return _roles; + } + + internal set { + _roles = value; + } + } + + /// + /// Gets the user name associated with the credentials. + /// + /// + /// A that represents the user name associated with the + /// credentials. + /// + public string UserName { + get { + return _username; + } + + internal set { + _username = value; + } + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/QueryStringCollection.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/QueryStringCollection.cs new file mode 100644 index 0000000..6a2285f --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/QueryStringCollection.cs @@ -0,0 +1,68 @@ +#region License +/* + * QueryStringCollection.cs + * + * This code is derived from System.Net.HttpUtility.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005-2009 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Patrik Torstensson + * - Wictor Wilén (decode/encode functions) + * - Tim Coleman + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; +using System.Collections.Specialized; +using System.Text; + +namespace WebSocketSharp.Net +{ + internal sealed class QueryStringCollection : NameValueCollection + { + public override string ToString () + { + var cnt = Count; + if (cnt == 0) + return String.Empty; + + var output = new StringBuilder (); + var keys = AllKeys; + foreach (var key in keys) + output.AppendFormat ("{0}={1}&", key, this [key]); + + if (output.Length > 0) + output.Length--; + + return output.ToString (); + } + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/ReadBufferState.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/ReadBufferState.cs new file mode 100644 index 0000000..0609c28 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/ReadBufferState.cs @@ -0,0 +1,84 @@ +#region License +/* + * ReadBufferState.cs + * + * This code is derived from System.Net.ChunkedInputStream.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; + +namespace WebSocketSharp.Net +{ + internal class ReadBufferState + { + #region Public Constructors + + public ReadBufferState ( + byte [] buffer, int offset, int count, HttpStreamAsyncResult asyncResult) + { + Buffer = buffer; + Offset = offset; + Count = count; + InitialCount = count; + AsyncResult = asyncResult; + } + + #endregion + + #region Public Properties + + public HttpStreamAsyncResult AsyncResult { + get; set; + } + + public byte [] Buffer { + get; set; + } + + public int Count { + get; set; + } + + public int InitialCount { + get; set; + } + + public int Offset { + get; set; + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/RequestStream.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/RequestStream.cs new file mode 100644 index 0000000..a27eab0 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/RequestStream.cs @@ -0,0 +1,275 @@ +#region License +/* + * RequestStream.cs + * + * This code is derived from System.Net.RequestStream.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; +using System.IO; + +namespace WebSocketSharp.Net +{ + internal class RequestStream : Stream + { + #region Private Fields + + private byte [] _buffer; + private bool _disposed; + private int _length; + private int _offset; + private long _remainingBody; + private Stream _stream; + + #endregion + + #region Internal Constructors + + internal RequestStream (Stream stream, byte [] buffer, int offset, int length) + : this (stream, buffer, offset, length, -1) + { + } + + internal RequestStream ( + Stream stream, byte [] buffer, int offset, int length, long contentlength) + { + _stream = stream; + _buffer = buffer; + _offset = offset; + _length = length; + _remainingBody = contentlength; + } + + #endregion + + #region Public Properties + + public override bool CanRead { + get { + return true; + } + } + + public override bool CanSeek { + get { + return false; + } + } + + public override bool CanWrite { + get { + return false; + } + } + + public override long Length { + get { + throw new NotSupportedException (); + } + } + + public override long Position { + get { + throw new NotSupportedException (); + } + + set { + throw new NotSupportedException (); + } + } + + #endregion + + #region Private Methods + + // Returns 0 if we can keep reading from the base stream, + // > 0 if we read something from the buffer. + // -1 if we had a content length set and we finished reading that many bytes. + private int fillFromBuffer (byte [] buffer, int offset, int count) + { + if (buffer == null) + throw new ArgumentNullException ("buffer"); + + if (offset < 0) + throw new ArgumentOutOfRangeException ("offset", "Less than zero."); + + if (count < 0) + throw new ArgumentOutOfRangeException ("count", "Less than zero."); + + var len = buffer.Length; + if (offset > len) + throw new ArgumentException ("'offset' is greater than 'buffer' size."); + + if (offset > len - count) + throw new ArgumentException ("Reading would overrun 'buffer'."); + + if (_remainingBody == 0) + return -1; + + if (_length == 0) + return 0; + + var size = _length < count ? _length : count; + if (_remainingBody > 0 && _remainingBody < size) + size = (int) _remainingBody; + + var remainingBuffer = _buffer.Length - _offset; + if (remainingBuffer < size) + size = remainingBuffer; + + if (size == 0) + return 0; + + Buffer.BlockCopy (_buffer, _offset, buffer, offset, size); + _offset += size; + _length -= size; + if (_remainingBody > 0) + _remainingBody -= size; + + return size; + } + + #endregion + + #region Public Methods + + public override IAsyncResult BeginRead ( + byte [] buffer, int offset, int count, AsyncCallback callback, object state) + { + if (_disposed) + throw new ObjectDisposedException (GetType ().ToString ()); + + var nread = fillFromBuffer (buffer, offset, count); + if (nread > 0 || nread == -1) { + var ares = new HttpStreamAsyncResult (callback, state); + ares.Buffer = buffer; + ares.Offset = offset; + ares.Count = count; + ares.SyncRead = nread; + ares.Complete (); + + return ares; + } + + // Avoid reading past the end of the request to allow for HTTP pipelining. + if (_remainingBody >= 0 && _remainingBody < count) + count = (int) _remainingBody; + + return _stream.BeginRead (buffer, offset, count, callback, state); + } + + public override IAsyncResult BeginWrite ( + byte [] buffer, int offset, int count, AsyncCallback callback, object state) + { + throw new NotSupportedException (); + } + + public override void Close () + { + _disposed = true; + } + + public override int EndRead (IAsyncResult asyncResult) + { + if (_disposed) + throw new ObjectDisposedException (GetType ().ToString ()); + + if (asyncResult == null) + throw new ArgumentNullException ("asyncResult"); + + if (asyncResult is HttpStreamAsyncResult) { + var ares = (HttpStreamAsyncResult) asyncResult; + if (!ares.IsCompleted) + ares.AsyncWaitHandle.WaitOne (); + + return ares.SyncRead; + } + + // Close on exception? + var nread = _stream.EndRead (asyncResult); + if (nread > 0 && _remainingBody > 0) + _remainingBody -= nread; + + return nread; + } + + public override void EndWrite (IAsyncResult asyncResult) + { + throw new NotSupportedException (); + } + + public override void Flush () + { + } + + public override int Read (byte [] buffer, int offset, int count) + { + if (_disposed) + throw new ObjectDisposedException (GetType ().ToString ()); + + // Call fillFromBuffer to check for buffer boundaries even when + // _remainingBody is 0. + var nread = fillFromBuffer (buffer, offset, count); + if (nread == -1) // No more bytes available (Content-Length). + return 0; + + if (nread > 0) + return nread; + + nread = _stream.Read (buffer, offset, count); + if (nread > 0 && _remainingBody > 0) + _remainingBody -= nread; + + return nread; + } + + public override long Seek (long offset, SeekOrigin origin) + { + throw new NotSupportedException (); + } + + public override void SetLength (long value) + { + throw new NotSupportedException (); + } + + public override void Write (byte [] buffer, int offset, int count) + { + throw new NotSupportedException (); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/ResponseStream.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/ResponseStream.cs new file mode 100644 index 0000000..59a60d3 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/ResponseStream.cs @@ -0,0 +1,314 @@ +#region License +/* + * ResponseStream.cs + * + * This code is derived from System.Net.ResponseStream.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Gonzalo Paniagua Javier + */ +#endregion + +using System; +using System.IO; +using System.Text; + +namespace WebSocketSharp.Net +{ + // FIXME: Does this buffer the response until Close? + // Update: we send a single packet for the first non-chunked Write + // What happens when we set content-length to X and write X-1 bytes then close? + // what if we don't set content-length at all? + internal class ResponseStream : Stream + { + #region Private Static Fields + + private static byte [] _crlf = new byte [] { 13, 10 }; + + #endregion + + #region Private Fields + + private bool _disposed; + private bool _ignoreErrors; + private HttpListenerResponse _response; + private Stream _stream; + private bool _trailerSent; + + #endregion + + #region Internal Constructors + + internal ResponseStream (Stream stream, HttpListenerResponse response, bool ignoreErrors) + { + _stream = stream; + _response = response; + _ignoreErrors = ignoreErrors; + } + + #endregion + + #region Public Properties + + public override bool CanRead { + get { + return false; + } + } + + public override bool CanSeek { + get { + return false; + } + } + + public override bool CanWrite { + get { + return true; + } + } + + public override long Length { + get { + throw new NotSupportedException (); + } + } + + public override long Position { + get { + throw new NotSupportedException (); + } + + set { + throw new NotSupportedException (); + } + } + + #endregion + + #region Private Methods + + private static byte [] getChunkSizeBytes (int size, bool final) + { + return Encoding.ASCII.GetBytes (String.Format ("{0:x}\r\n{1}", size, final ? "\r\n" : "")); + } + + private MemoryStream getHeaders (bool closing) + { + if (_response.HeadersSent) + return null; + + var stream = new MemoryStream (); + _response.SendHeaders (closing, stream); + + return stream; + } + + #endregion + + #region Internal Methods + + internal void WriteInternally (byte [] buffer, int offset, int count) + { + if (_ignoreErrors) { + try { + _stream.Write (buffer, offset, count); + } + catch { + } + } + else { + _stream.Write (buffer, offset, count); + } + } + + #endregion + + #region Public Methods + + public override IAsyncResult BeginRead ( + byte [] buffer, int offset, int count, AsyncCallback callback, object state) + { + throw new NotSupportedException (); + } + + public override IAsyncResult BeginWrite ( + byte [] buffer, int offset, int count, AsyncCallback callback, object state) + { + if (_disposed) + throw new ObjectDisposedException (GetType ().ToString ()); + + var headers = getHeaders (false); + var chunked = _response.SendChunked; + byte [] bytes = null; + if (headers != null) { + using (headers) { + var start = headers.Position; + headers.Position = headers.Length; + if (chunked) { + bytes = getChunkSizeBytes (count, false); + headers.Write (bytes, 0, bytes.Length); + } + + headers.Write (buffer, offset, count); + buffer = headers.GetBuffer (); + offset = (int) start; + count = (int) (headers.Position - start); + } + } + else if (chunked) { + bytes = getChunkSizeBytes (count, false); + WriteInternally (bytes, 0, bytes.Length); + } + + return _stream.BeginWrite (buffer, offset, count, callback, state); + } + + public override void Close () + { + if (_disposed) + return; + + _disposed = true; + + var headers = getHeaders (true); + var chunked = _response.SendChunked; + byte [] bytes = null; + if (headers != null) { + using (headers) { + var start = headers.Position; + if (chunked && !_trailerSent) { + bytes = getChunkSizeBytes (0, true); + headers.Position = headers.Length; + headers.Write (bytes, 0, bytes.Length); + } + + WriteInternally (headers.GetBuffer (), (int) start, (int) (headers.Length - start)); + } + + _trailerSent = true; + } + else if (chunked && !_trailerSent) { + bytes = getChunkSizeBytes (0, true); + WriteInternally (bytes, 0, bytes.Length); + _trailerSent = true; + } + + _response.Close (); + } + + public override int EndRead (IAsyncResult asyncResult) + { + throw new NotSupportedException (); + } + + public override void EndWrite (IAsyncResult asyncResult) + { + if (_disposed) + throw new ObjectDisposedException (GetType ().ToString ()); + + Action endWrite = ares => { + _stream.EndWrite (ares); + if (_response.SendChunked) + _stream.Write (_crlf, 0, 2); + }; + + if (_ignoreErrors) { + try { + endWrite (asyncResult); + } + catch { + } + } + else { + endWrite (asyncResult); + } + } + + public override void Flush () + { + } + + public override int Read (byte [] buffer, int offset, int count) + { + throw new NotSupportedException (); + } + + public override long Seek (long offset, SeekOrigin origin) + { + throw new NotSupportedException (); + } + + public override void SetLength (long value) + { + throw new NotSupportedException (); + } + + public override void Write (byte [] buffer, int offset, int count) + { + if (_disposed) + throw new ObjectDisposedException (GetType ().ToString ()); + + var headers = getHeaders (false); + var chunked = _response.SendChunked; + byte [] bytes = null; + if (headers != null) { + // After the possible preamble for the encoding. + using (headers) { + var start = headers.Position; + headers.Position = headers.Length; + if (chunked) { + bytes = getChunkSizeBytes (count, false); + headers.Write (bytes, 0, bytes.Length); + } + + var newCount = Math.Min (count, 16384 - (int) headers.Position + (int) start); + headers.Write (buffer, offset, newCount); + count -= newCount; + offset += newCount; + WriteInternally (headers.GetBuffer (), (int) start, (int) (headers.Length - start)); + } + } + else if (chunked) { + bytes = getChunkSizeBytes (count, false); + WriteInternally (bytes, 0, bytes.Length); + } + + if (count > 0) + WriteInternally (buffer, offset, count); + + if (chunked) + WriteInternally (_crlf, 0, 2); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/Security/SslStream.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/Security/SslStream.cs new file mode 100644 index 0000000..7588eee --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/Security/SslStream.cs @@ -0,0 +1,82 @@ +#region License +/* + * SslStream.cs + * + * The MIT License + * + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Net.Security; +using System.Net.Sockets; + +namespace WebSocketSharp.Net.Security +{ + internal class SslStream : System.Net.Security.SslStream + { + #region Public Constructors + + public SslStream (NetworkStream innerStream) + : base (innerStream) + { + } + + public SslStream (NetworkStream innerStream, bool leaveInnerStreamOpen) + : base (innerStream, leaveInnerStreamOpen) + { + } + + public SslStream ( + NetworkStream innerStream, + bool leaveInnerStreamOpen, + RemoteCertificateValidationCallback userCertificateValidationCallback) + : base (innerStream, leaveInnerStreamOpen, userCertificateValidationCallback) + { + } + + public SslStream ( + NetworkStream innerStream, + bool leaveInnerStreamOpen, + RemoteCertificateValidationCallback userCertificateValidationCallback, + LocalCertificateSelectionCallback userCertificateSelectionCallback) + : base ( + innerStream, + leaveInnerStreamOpen, + userCertificateValidationCallback, + userCertificateSelectionCallback) + { + } + + #endregion + + #region Public Properties + + public bool DataAvailable { + get { + return ((NetworkStream) InnerStream).DataAvailable; + } + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/WebHeaderCollection.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/WebHeaderCollection.cs new file mode 100644 index 0000000..488bee9 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/WebHeaderCollection.cs @@ -0,0 +1,1536 @@ +#region License +/* + * WebHeaderCollection.cs + * + * This code is derived from System.Net.WebHeaderCollection.cs of Mono + * (http://www.mono-project.com). + * + * The MIT License + * + * Copyright (c) 2003 Ximian, Inc. (http://www.ximian.com) + * Copyright (c) 2007 Novell, Inc. (http://www.novell.com) + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +#region Authors +/* + * Authors: + * - Lawrence Pit + * - Gonzalo Paniagua Javier + * - Miguel de Icaza + */ +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Net; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Security.Permissions; +using System.Text; + +namespace WebSocketSharp.Net +{ + /// + /// Provides a collection of the HTTP headers associated with a request or response. + /// + [Serializable] + [ComVisible (true)] + public class WebHeaderCollection : NameValueCollection, ISerializable + { + #region Private Static Fields + + private static readonly Dictionary _headers; + + #endregion + + #region Private Fields + + private bool _internallyCreated; + private HttpHeaderType _state; + + #endregion + + #region Static Constructor + + static WebHeaderCollection () + { + _headers = + new Dictionary (StringComparer.InvariantCultureIgnoreCase) { + { + "Accept", + new HttpHeaderInfo () { + Name = "Accept", + Type = HttpHeaderType.Request | HttpHeaderType.Restricted | HttpHeaderType.MultiValue + } + }, + { + "AcceptCharset", + new HttpHeaderInfo () { + Name = "Accept-Charset", + Type = HttpHeaderType.Request | HttpHeaderType.MultiValue + } + }, + { + "AcceptEncoding", + new HttpHeaderInfo () { + Name = "Accept-Encoding", + Type = HttpHeaderType.Request | HttpHeaderType.MultiValue + } + }, + { + "AcceptLanguage", + new HttpHeaderInfo () { + Name = "Accept-language", + Type = HttpHeaderType.Request | HttpHeaderType.MultiValue + } + }, + { + "AcceptRanges", + new HttpHeaderInfo () { + Name = "Accept-Ranges", + Type = HttpHeaderType.Response | HttpHeaderType.MultiValue + } + }, + { + "Age", + new HttpHeaderInfo () { + Name = "Age", + Type = HttpHeaderType.Response + } + }, + { + "Allow", + new HttpHeaderInfo () { + Name = "Allow", + Type = HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.MultiValue + } + }, + { + "Authorization", + new HttpHeaderInfo () { + Name = "Authorization", + Type = HttpHeaderType.Request | HttpHeaderType.MultiValue + } + }, + { + "CacheControl", + new HttpHeaderInfo () { + Name = "Cache-Control", + Type = HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.MultiValue + } + }, + { + "Connection", + new HttpHeaderInfo () { + Name = "Connection", + Type = HttpHeaderType.Request | + HttpHeaderType.Response | + HttpHeaderType.Restricted | + HttpHeaderType.MultiValue + } + }, + { + "ContentEncoding", + new HttpHeaderInfo () { + Name = "Content-Encoding", + Type = HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.MultiValue + } + }, + { + "ContentLanguage", + new HttpHeaderInfo () { + Name = "Content-Language", + Type = HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.MultiValue + } + }, + { + "ContentLength", + new HttpHeaderInfo () { + Name = "Content-Length", + Type = HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.Restricted + } + }, + { + "ContentLocation", + new HttpHeaderInfo () { + Name = "Content-Location", + Type = HttpHeaderType.Request | HttpHeaderType.Response + } + }, + { + "ContentMd5", + new HttpHeaderInfo () { + Name = "Content-MD5", + Type = HttpHeaderType.Request | HttpHeaderType.Response + } + }, + { + "ContentRange", + new HttpHeaderInfo () { + Name = "Content-Range", + Type = HttpHeaderType.Request | HttpHeaderType.Response + } + }, + { + "ContentType", + new HttpHeaderInfo () { + Name = "Content-Type", + Type = HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.Restricted + } + }, + { + "Cookie", + new HttpHeaderInfo () { + Name = "Cookie", + Type = HttpHeaderType.Request + } + }, + { + "Cookie2", + new HttpHeaderInfo () { + Name = "Cookie2", + Type = HttpHeaderType.Request + } + }, + { + "Date", + new HttpHeaderInfo () { + Name = "Date", + Type = HttpHeaderType.Request | + HttpHeaderType.Response | + HttpHeaderType.Restricted + } + }, + { + "Expect", + new HttpHeaderInfo () { + Name = "Expect", + Type = HttpHeaderType.Request | HttpHeaderType.Restricted | HttpHeaderType.MultiValue + } + }, + { + "Expires", + new HttpHeaderInfo () { + Name = "Expires", + Type = HttpHeaderType.Request | HttpHeaderType.Response + } + }, + { + "ETag", + new HttpHeaderInfo () { + Name = "ETag", + Type = HttpHeaderType.Response + } + }, + { + "From", + new HttpHeaderInfo () { + Name = "From", + Type = HttpHeaderType.Request + } + }, + { + "Host", + new HttpHeaderInfo () { + Name = "Host", + Type = HttpHeaderType.Request | HttpHeaderType.Restricted + } + }, + { + "IfMatch", + new HttpHeaderInfo () { + Name = "If-Match", + Type = HttpHeaderType.Request | HttpHeaderType.MultiValue + } + }, + { + "IfModifiedSince", + new HttpHeaderInfo () { + Name = "If-Modified-Since", + Type = HttpHeaderType.Request | HttpHeaderType.Restricted + } + }, + { + "IfNoneMatch", + new HttpHeaderInfo () { + Name = "If-None-Match", + Type = HttpHeaderType.Request | HttpHeaderType.MultiValue + } + }, + { + "IfRange", + new HttpHeaderInfo () { + Name = "If-Range", + Type = HttpHeaderType.Request + } + }, + { + "IfUnmodifiedSince", + new HttpHeaderInfo () { + Name = "If-Unmodified-Since", + Type = HttpHeaderType.Request + } + }, + { + "KeepAlive", + new HttpHeaderInfo () { + Name = "Keep-Alive", + Type = HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.MultiValue + } + }, + { + "LastModified", + new HttpHeaderInfo () { + Name = "Last-Modified", + Type = HttpHeaderType.Request | HttpHeaderType.Response + } + }, + { + "Location", + new HttpHeaderInfo () { + Name = "Location", + Type = HttpHeaderType.Response + } + }, + { + "MaxForwards", + new HttpHeaderInfo () { + Name = "Max-Forwards", + Type = HttpHeaderType.Request + } + }, + { + "Pragma", + new HttpHeaderInfo () { + Name = "Pragma", + Type = HttpHeaderType.Request | HttpHeaderType.Response + } + }, + { + "ProxyConnection", + new HttpHeaderInfo () { + Name = "Proxy-Connection", + Type = HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.Restricted + } + }, + { + "ProxyAuthenticate", + new HttpHeaderInfo () { + Name = "Proxy-Authenticate", + Type = HttpHeaderType.Response | HttpHeaderType.MultiValue + } + }, + { + "ProxyAuthorization", + new HttpHeaderInfo () { + Name = "Proxy-Authorization", + Type = HttpHeaderType.Request + } + }, + { + "Public", + new HttpHeaderInfo () { + Name = "Public", + Type = HttpHeaderType.Response | HttpHeaderType.MultiValue + } + }, + { + "Range", + new HttpHeaderInfo () { + Name = "Range", + Type = HttpHeaderType.Request | HttpHeaderType.Restricted | HttpHeaderType.MultiValue + } + }, + { + "Referer", + new HttpHeaderInfo () { + Name = "Referer", + Type = HttpHeaderType.Request | HttpHeaderType.Restricted + } + }, + { + "RetryAfter", + new HttpHeaderInfo () { + Name = "Retry-After", + Type = HttpHeaderType.Response + } + }, + { + "SecWebSocketAccept", + new HttpHeaderInfo () { + Name = "Sec-WebSocket-Accept", + Type = HttpHeaderType.Response | HttpHeaderType.Restricted + } + }, + { + "SecWebSocketExtensions", + new HttpHeaderInfo () { + Name = "Sec-WebSocket-Extensions", + Type = HttpHeaderType.Request | + HttpHeaderType.Response | + HttpHeaderType.Restricted | + HttpHeaderType.MultiValueInRequest + } + }, + { + "SecWebSocketKey", + new HttpHeaderInfo () { + Name = "Sec-WebSocket-Key", + Type = HttpHeaderType.Request | HttpHeaderType.Restricted + } + }, + { + "SecWebSocketProtocol", + new HttpHeaderInfo () { + Name = "Sec-WebSocket-Protocol", + Type = HttpHeaderType.Request | + HttpHeaderType.Response | + HttpHeaderType.MultiValueInRequest + } + }, + { + "SecWebSocketVersion", + new HttpHeaderInfo () { + Name = "Sec-WebSocket-Version", + Type = HttpHeaderType.Request | + HttpHeaderType.Response | + HttpHeaderType.Restricted | + HttpHeaderType.MultiValueInResponse + } + }, + { + "Server", + new HttpHeaderInfo () { + Name = "Server", + Type = HttpHeaderType.Response + } + }, + { + "SetCookie", + new HttpHeaderInfo () { + Name = "Set-Cookie", + Type = HttpHeaderType.Response | HttpHeaderType.MultiValue + } + }, + { + "SetCookie2", + new HttpHeaderInfo () { + Name = "Set-Cookie2", + Type = HttpHeaderType.Response | HttpHeaderType.MultiValue + } + }, + { + "Te", + new HttpHeaderInfo () { + Name = "TE", + Type = HttpHeaderType.Request + } + }, + { + "Trailer", + new HttpHeaderInfo () { + Name = "Trailer", + Type = HttpHeaderType.Request | HttpHeaderType.Response + } + }, + { + "TransferEncoding", + new HttpHeaderInfo () { + Name = "Transfer-Encoding", + Type = HttpHeaderType.Request | + HttpHeaderType.Response | + HttpHeaderType.Restricted | + HttpHeaderType.MultiValue + } + }, + { + "Translate", + new HttpHeaderInfo () { + Name = "Translate", + Type = HttpHeaderType.Request + } + }, + { + "Upgrade", + new HttpHeaderInfo () { + Name = "Upgrade", + Type = HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.MultiValue + } + }, + { + "UserAgent", + new HttpHeaderInfo () { + Name = "User-Agent", + Type = HttpHeaderType.Request | HttpHeaderType.Restricted + } + }, + { + "Vary", + new HttpHeaderInfo () { + Name = "Vary", + Type = HttpHeaderType.Response | HttpHeaderType.MultiValue + } + }, + { + "Via", + new HttpHeaderInfo () { + Name = "Via", + Type = HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.MultiValue + } + }, + { + "Warning", + new HttpHeaderInfo () { + Name = "Warning", + Type = HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.MultiValue + } + }, + { + "WwwAuthenticate", + new HttpHeaderInfo () { + Name = "WWW-Authenticate", + Type = HttpHeaderType.Response | HttpHeaderType.Restricted | HttpHeaderType.MultiValue + } + } + }; + } + + #endregion + + #region Internal Constructors + + internal WebHeaderCollection (bool internallyCreated) + { + _internallyCreated = internallyCreated; + _state = HttpHeaderType.Unspecified; + } + + #endregion + + #region Protected Constructors + + /// + /// Initializes a new instance of the class from + /// the specified and . + /// + /// + /// A that contains the serialized object data. + /// + /// + /// A that specifies the source for the deserialization. + /// + /// + /// is . + /// + /// + /// An element with the specified name isn't found in . + /// + protected WebHeaderCollection ( + SerializationInfo serializationInfo, StreamingContext streamingContext) + { + if (serializationInfo == null) + throw new ArgumentNullException ("serializationInfo"); + + try { + _internallyCreated = serializationInfo.GetBoolean ("InternallyCreated"); + _state = (HttpHeaderType) serializationInfo.GetInt32 ("State"); + + var count = serializationInfo.GetInt32 ("Count"); + for (int i = 0; i < count; i++) { + base.Add ( + serializationInfo.GetString (i.ToString ()), + serializationInfo.GetString ((count + i).ToString ())); + } + } + catch (SerializationException ex) { + throw new ArgumentException (ex.Message, "serializationInfo", ex); + } + } + + #endregion + + #region Public Constructors + + /// + /// Initializes a new instance of the class. + /// + public WebHeaderCollection () + { + _internallyCreated = false; + _state = HttpHeaderType.Unspecified; + } + + #endregion + + #region Public Properties + + /// + /// Gets all header names in the collection. + /// + /// + /// An array of that contains all header names in the collection. + /// + public override string [] AllKeys { + get { + return base.AllKeys; + } + } + + /// + /// Gets the number of headers in the collection. + /// + /// + /// An that represents the number of headers in the collection. + /// + public override int Count { + get { + return base.Count; + } + } + + /// + /// Gets or sets the specified request in the collection. + /// + /// + /// A that represents the value of the request . + /// + /// + /// One of the enum values, represents the request header + /// to get or set. + /// + /// + /// + /// is a restricted header. + /// + /// + /// -or- + /// + /// + /// contains invalid characters. + /// + /// + /// + /// The length of is greater than 65,535 characters. + /// + /// + /// The current instance doesn't allow the request + /// . + /// + public string this [HttpRequestHeader header] { + get { + return Get (Convert (header)); + } + + set { + Add (header, value); + } + } + + /// + /// Gets or sets the specified response in the collection. + /// + /// + /// A that represents the value of the response . + /// + /// + /// One of the enum values, represents the response header + /// to get or set. + /// + /// + /// + /// is a restricted header. + /// + /// + /// -or- + /// + /// + /// contains invalid characters. + /// + /// + /// + /// The length of is greater than 65,535 characters. + /// + /// + /// The current instance doesn't allow the response + /// . + /// + public string this [HttpResponseHeader header] { + get { + return Get (Convert (header)); + } + + set { + Add (header, value); + } + } + + /// + /// Gets a collection of header names in the collection. + /// + /// + /// A that contains all header names + /// in the collection. + /// + public override NameObjectCollectionBase.KeysCollection Keys { + get { + return base.Keys; + } + } + + #endregion + + #region Private Methods + + private void add (string name, string value, bool ignoreRestricted) + { + Action act; + if (ignoreRestricted) + act = addWithoutCheckingNameAndRestricted; + else + act = addWithoutCheckingName; + + doWithCheckingState (act, checkName (name), value, true); + } + + private void addWithoutCheckingName (string name, string value) + { + doWithoutCheckingName (base.Add, name, value); + } + + private void addWithoutCheckingNameAndRestricted (string name, string value) + { + base.Add (name, checkValue (value)); + } + + private static int checkColonSeparated (string header) + { + var i = header.IndexOf (':'); + if (i == -1) + throw new ArgumentException ("No colon found.", "header"); + + return i; + } + + private static HttpHeaderType checkHeaderType (string name) + { + var info = getHeaderInfo (name); + return info == null + ? HttpHeaderType.Unspecified + : info.IsRequest && !info.IsResponse + ? HttpHeaderType.Request + : !info.IsRequest && info.IsResponse + ? HttpHeaderType.Response + : HttpHeaderType.Unspecified; + } + + private static string checkName (string name) + { + if (name == null || name.Length == 0) + throw new ArgumentNullException ("name"); + + name = name.Trim (); + if (!IsHeaderName (name)) + throw new ArgumentException ("Contains invalid characters.", "name"); + + return name; + } + + private void checkRestricted (string name) + { + if (!_internallyCreated && isRestricted (name, true)) + throw new ArgumentException ("This header must be modified with the appropiate property."); + } + + private void checkState (bool response) + { + if (_state == HttpHeaderType.Unspecified) + return; + + if (response && _state == HttpHeaderType.Request) + throw new InvalidOperationException ( + "This collection has already been used to store the request headers."); + + if (!response && _state == HttpHeaderType.Response) + throw new InvalidOperationException ( + "This collection has already been used to store the response headers."); + } + + private static string checkValue (string value) + { + if (value == null || value.Length == 0) + return String.Empty; + + value = value.Trim (); + if (value.Length > 65535) + throw new ArgumentOutOfRangeException ("value", "Greater than 65,535 characters."); + + if (!IsHeaderValue (value)) + throw new ArgumentException ("Contains invalid characters.", "value"); + + return value; + } + + private static string convert (string key) + { + HttpHeaderInfo info; + return _headers.TryGetValue (key, out info) + ? info.Name + : String.Empty; + } + + private void doWithCheckingState ( + Action action, string name, string value, bool setState) + { + var type = checkHeaderType (name); + if (type == HttpHeaderType.Request) + doWithCheckingState (action, name, value, false, setState); + else if (type == HttpHeaderType.Response) + doWithCheckingState (action, name, value, true, setState); + else + action (name, value); + } + + private void doWithCheckingState ( + Action action, string name, string value, bool response, bool setState) + { + checkState (response); + action (name, value); + if (setState && _state == HttpHeaderType.Unspecified) + _state = response ? HttpHeaderType.Response : HttpHeaderType.Request; + } + + private void doWithoutCheckingName (Action action, string name, string value) + { + checkRestricted (name); + action (name, checkValue (value)); + } + + private static HttpHeaderInfo getHeaderInfo (string name) + { + foreach (var info in _headers.Values) + if (info.Name.Equals (name, StringComparison.InvariantCultureIgnoreCase)) + return info; + + return null; + } + + private static bool isRestricted (string name, bool response) + { + var info = getHeaderInfo (name); + return info != null && info.IsRestricted (response); + } + + private void removeWithoutCheckingName (string name, string unuse) + { + checkRestricted (name); + base.Remove (name); + } + + private void setWithoutCheckingName (string name, string value) + { + doWithoutCheckingName (base.Set, name, value); + } + + #endregion + + #region Internal Methods + + internal static string Convert (HttpRequestHeader header) + { + return convert (header.ToString ()); + } + + internal static string Convert (HttpResponseHeader header) + { + return convert (header.ToString ()); + } + + internal static bool IsHeaderName (string name) + { + return name != null && name.Length > 0 && name.IsToken (); + } + + internal static bool IsHeaderValue (string value) + { + return value.IsText (); + } + + internal static bool IsMultiValue (string headerName, bool response) + { + if (headerName == null || headerName.Length == 0) + return false; + + var info = getHeaderInfo (headerName); + return info != null && info.IsMultiValue (response); + } + + internal void RemoveInternally (string name) + { + base.Remove (name); + } + + internal void SetInternally (string header, bool response) + { + var pos = checkColonSeparated (header); + SetInternally (header.Substring (0, pos), header.Substring (pos + 1), response); + } + + internal void SetInternally (string name, string value, bool response) + { + value = checkValue (value); + if (IsMultiValue (name, response)) + base.Add (name, value); + else + base.Set (name, value); + } + + internal string ToStringMultiValue (bool response) + { + var buff = new StringBuilder (); + Count.Times ( + i => { + var key = GetKey (i); + if (IsMultiValue (key, response)) + foreach (var value in GetValues (i)) + buff.AppendFormat ("{0}: {1}\r\n", key, value); + else + buff.AppendFormat ("{0}: {1}\r\n", key, Get (i)); + }); + + return buff.Append ("\r\n").ToString (); + } + + #endregion + + #region Protected Methods + + /// + /// Adds a header to the collection without checking whether the header is on the restricted + /// header list. + /// + /// + /// A that represents the name of the header to add. + /// + /// + /// A that represents the value of the header to add. + /// + /// + /// is or empty. + /// + /// + /// or contains invalid characters. + /// + /// + /// The length of is greater than 65,535 characters. + /// + /// + /// The current instance doesn't allow + /// the . + /// + protected void AddWithoutValidate (string headerName, string headerValue) + { + add (headerName, headerValue, true); + } + + #endregion + + #region Public Methods + + /// + /// Adds the specified to the collection. + /// + /// + /// A that represents the header with the name and value separated by + /// a colon (:). + /// + /// + /// is , empty, or the name part of + /// is empty. + /// + /// + /// + /// doesn't contain a colon. + /// + /// + /// -or- + /// + /// + /// is a restricted header. + /// + /// + /// -or- + /// + /// + /// The name or value part of contains invalid characters. + /// + /// + /// + /// The length of the value part of is greater than 65,535 characters. + /// + /// + /// The current instance doesn't allow + /// the . + /// + public void Add (string header) + { + if (header.IsNullOrEmpty ()) + throw new ArgumentNullException ("header"); + + var pos = checkColonSeparated (header); + add (header.Substring (0, pos), header.Substring (pos + 1), false); + } + + /// + /// Adds the specified request with the specified + /// to the collection. + /// + /// + /// One of the enum values, represents the request header + /// to add. + /// + /// + /// A that represents the value of the header to add. + /// + /// + /// + /// is a restricted header. + /// + /// + /// -or- + /// + /// + /// contains invalid characters. + /// + /// + /// + /// The length of is greater than 65,535 characters. + /// + /// + /// The current instance doesn't allow the request + /// . + /// + public void Add (HttpRequestHeader header, string value) + { + doWithCheckingState (addWithoutCheckingName, Convert (header), value, false, true); + } + + /// + /// Adds the specified response with the specified + /// to the collection. + /// + /// + /// One of the enum values, represents the response header + /// to add. + /// + /// + /// A that represents the value of the header to add. + /// + /// + /// + /// is a restricted header. + /// + /// + /// -or- + /// + /// + /// contains invalid characters. + /// + /// + /// + /// The length of is greater than 65,535 characters. + /// + /// + /// The current instance doesn't allow the response + /// . + /// + public void Add (HttpResponseHeader header, string value) + { + doWithCheckingState (addWithoutCheckingName, Convert (header), value, true, true); + } + + /// + /// Adds a header with the specified and + /// to the collection. + /// + /// + /// A that represents the name of the header to add. + /// + /// + /// A that represents the value of the header to add. + /// + /// + /// is or empty. + /// + /// + /// + /// or contains invalid characters. + /// + /// + /// -or- + /// + /// + /// is a restricted header name. + /// + /// + /// + /// The length of is greater than 65,535 characters. + /// + /// + /// The current instance doesn't allow the header + /// . + /// + public override void Add (string name, string value) + { + add (name, value, false); + } + + /// + /// Removes all headers from the collection. + /// + public override void Clear () + { + base.Clear (); + _state = HttpHeaderType.Unspecified; + } + + /// + /// Get the value of the header at the specified in the collection. + /// + /// + /// A that receives the value of the header. + /// + /// + /// An that represents the zero-based index of the header to find. + /// + /// + /// is out of allowable range of indexes for the collection. + /// + public override string Get (int index) + { + return base.Get (index); + } + + /// + /// Get the value of the header with the specified in the collection. + /// + /// + /// A that receives the value of the header if found; otherwise, + /// . + /// + /// + /// A that represents the name of the header to find. + /// + public override string Get (string name) + { + return base.Get (name); + } + + /// + /// Gets the enumerator used to iterate through the collection. + /// + /// + /// An instance used to iterate through the collection. + /// + public override IEnumerator GetEnumerator () + { + return base.GetEnumerator (); + } + + /// + /// Get the name of the header at the specified in the collection. + /// + /// + /// A that receives the header name. + /// + /// + /// An that represents the zero-based index of the header to find. + /// + /// + /// is out of allowable range of indexes for the collection. + /// + public override string GetKey (int index) + { + return base.GetKey (index); + } + + /// + /// Gets an array of header values stored in the specified position + /// of the collection. + /// + /// + /// An array of that receives the header values if found; otherwise, + /// . + /// + /// + /// An that represents the zero-based index of the header to find. + /// + /// + /// is out of allowable range of indexes for the collection. + /// + public override string [] GetValues (int index) + { + var values = base.GetValues (index); + return values != null && values.Length > 0 + ? values + : null; + } + + /// + /// Gets an array of header values stored in the specified . + /// + /// + /// An array of that receives the header values if found; otherwise, + /// . + /// + /// + /// A that represents the name of the header to find. + /// + public override string [] GetValues (string header) + { + var values = base.GetValues (header); + return values != null && values.Length > 0 + ? values + : null; + } + + /// + /// Populates the specified with the data needed to serialize + /// the . + /// + /// + /// A that holds the serialized object data. + /// + /// + /// A that specifies the destination for the serialization. + /// + /// + /// is . + /// + /** + * FIXME: Removed to avoid Unity warning + * [SecurityPermission ( + * SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] + */ + public override void GetObjectData ( + SerializationInfo serializationInfo, StreamingContext streamingContext) + { + if (serializationInfo == null) + throw new ArgumentNullException ("serializationInfo"); + + serializationInfo.AddValue ("InternallyCreated", _internallyCreated); + serializationInfo.AddValue ("State", (int) _state); + + var count = Count; + serializationInfo.AddValue ("Count", count); + count.Times ( + i => { + serializationInfo.AddValue (i.ToString (), GetKey (i)); + serializationInfo.AddValue ((count + i).ToString (), Get (i)); + }); + } + + /// + /// Determines whether the specified header can be set for the request. + /// + /// + /// true if the header is restricted; otherwise, false. + /// + /// + /// A that represents the name of the header to test. + /// + /// + /// is or empty. + /// + /// + /// contains invalid characters. + /// + public static bool IsRestricted (string headerName) + { + return isRestricted (checkName (headerName), false); + } + + /// + /// Determines whether the specified header can be set for the request or the response. + /// + /// + /// true if the header is restricted; otherwise, false. + /// + /// + /// A that represents the name of the header to test. + /// + /// + /// true if does the test for the response; for the request, false. + /// + /// + /// is or empty. + /// + /// + /// contains invalid characters. + /// + public static bool IsRestricted (string headerName, bool response) + { + return isRestricted (checkName (headerName), response); + } + + /// + /// Implements the interface and raises the deserialization event + /// when the deserialization is complete. + /// + /// + /// An that represents the source of the deserialization event. + /// + public override void OnDeserialization (object sender) + { + } + + /// + /// Removes the specified request from the collection. + /// + /// + /// One of the enum values, represents the request header + /// to remove. + /// + /// + /// is a restricted header. + /// + /// + /// The current instance doesn't allow the request + /// . + /// + public void Remove (HttpRequestHeader header) + { + doWithCheckingState (removeWithoutCheckingName, Convert (header), null, false, false); + } + + /// + /// Removes the specified response from the collection. + /// + /// + /// One of the enum values, represents the response header + /// to remove. + /// + /// + /// is a restricted header. + /// + /// + /// The current instance doesn't allow the response + /// . + /// + public void Remove (HttpResponseHeader header) + { + doWithCheckingState (removeWithoutCheckingName, Convert (header), null, true, false); + } + + /// + /// Removes the specified header from the collection. + /// + /// + /// A that represents the name of the header to remove. + /// + /// + /// is or empty. + /// + /// + /// + /// contains invalid characters. + /// + /// + /// -or- + /// + /// + /// is a restricted header name. + /// + /// + /// + /// The current instance doesn't allow the header + /// . + /// + public override void Remove (string name) + { + doWithCheckingState (removeWithoutCheckingName, checkName (name), null, false); + } + + /// + /// Sets the specified request to the specified value. + /// + /// + /// One of the enum values, represents the request header + /// to set. + /// + /// + /// A that represents the value of the request header to set. + /// + /// + /// + /// is a restricted header. + /// + /// + /// -or- + /// + /// + /// contains invalid characters. + /// + /// + /// + /// The length of is greater than 65,535 characters. + /// + /// + /// The current instance doesn't allow the request + /// . + /// + public void Set (HttpRequestHeader header, string value) + { + doWithCheckingState (setWithoutCheckingName, Convert (header), value, false, true); + } + + /// + /// Sets the specified response to the specified value. + /// + /// + /// One of the enum values, represents the response header + /// to set. + /// + /// + /// A that represents the value of the response header to set. + /// + /// + /// + /// is a restricted header. + /// + /// + /// -or- + /// + /// + /// contains invalid characters. + /// + /// + /// + /// The length of is greater than 65,535 characters. + /// + /// + /// The current instance doesn't allow the response + /// . + /// + public void Set (HttpResponseHeader header, string value) + { + doWithCheckingState (setWithoutCheckingName, Convert (header), value, true, true); + } + + /// + /// Sets the specified header to the specified value. + /// + /// + /// A that represents the name of the header to set. + /// + /// + /// A that represents the value of the header to set. + /// + /// + /// is or empty. + /// + /// + /// + /// or contains invalid characters. + /// + /// + /// -or- + /// + /// + /// is a restricted header name. + /// + /// + /// + /// The length of is greater than 65,535 characters. + /// + /// + /// The current instance doesn't allow the header + /// . + /// + public override void Set (string name, string value) + { + doWithCheckingState (setWithoutCheckingName, checkName (name), value, true); + } + + /// + /// Converts the current to an array of . + /// + /// + /// An array of that receives the converted current + /// . + /// + public byte [] ToByteArray () + { + return Encoding.UTF8.GetBytes (ToString ()); + } + + /// + /// Returns a that represents the current + /// . + /// + /// + /// A that represents the current . + /// + public override string ToString () + { + var buff = new StringBuilder (); + Count.Times (i => buff.AppendFormat ("{0}: {1}\r\n", GetKey (i), Get (i))); + + return buff.Append ("\r\n").ToString (); + } + + #endregion + + #region Explicit Interface Implementation + + /// + /// Populates the specified with the data needed to serialize + /// the current . + /// + /// + /// A that holds the serialized object data. + /// + /// + /// A that specifies the destination for the serialization. + /// + /// + /// is . + /// + + /** + * FIXME: Removed to avoid Unity warnings + * [SecurityPermission ( + * SecurityAction.LinkDemand, + * Flags = SecurityPermissionFlag.SerializationFormatter, + * SerializationFormatter = true)] + */ + void ISerializable.GetObjectData ( + SerializationInfo serializationInfo, StreamingContext streamingContext) + { + GetObjectData (serializationInfo, streamingContext); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/WebSockets/HttpListenerWebSocketContext.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/WebSockets/HttpListenerWebSocketContext.cs new file mode 100644 index 0000000..83c32e2 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/WebSockets/HttpListenerWebSocketContext.cs @@ -0,0 +1,327 @@ +#region License +/* + * HttpListenerWebSocketContext.cs + * + * The MIT License + * + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Security.Principal; + +namespace WebSocketSharp.Net.WebSockets +{ + /// + /// Provides the properties used to access the information in a WebSocket connection request + /// received by the . + /// + /// + /// + public class HttpListenerWebSocketContext : WebSocketContext + { + #region Private Fields + + private HttpListenerContext _context; + private WebSocket _websocket; + + #endregion + + #region Internal Constructors + + internal HttpListenerWebSocketContext ( + HttpListenerContext context, string protocol, Logger logger) + { + _context = context; + _websocket = new WebSocket (this, protocol, logger); + } + + #endregion + + #region Internal Properties + + internal WebSocketStream Stream { + get { + return _context.Connection.GetWebSocketStream (); + } + } + + #endregion + + #region Public Properties + + /// + /// Gets the HTTP cookies included in the request. + /// + /// + /// A that contains the cookies. + /// + public override CookieCollection CookieCollection { + get { + return _context.Request.Cookies; + } + } + + /// + /// Gets the HTTP headers included in the request. + /// + /// + /// A that contains the headers. + /// + public override NameValueCollection Headers { + get { + return _context.Request.Headers; + } + } + + /// + /// Gets the value of the Host header included in the request. + /// + /// + /// A that represents the value of the Host header. + /// + public override string Host { + get { + return _context.Request.Headers ["Host"]; + } + } + + /// + /// Gets a value indicating whether the client is authenticated. + /// + /// + /// true if the client is authenticated; otherwise, false. + /// + public override bool IsAuthenticated { + get { + return _context.Request.IsAuthenticated; + } + } + + /// + /// Gets a value indicating whether the client connected from the local computer. + /// + /// + /// true if the client connected from the local computer; otherwise, false. + /// + public override bool IsLocal { + get { + return _context.Request.IsLocal; + } + } + + /// + /// Gets a value indicating whether the WebSocket connection is secured. + /// + /// + /// true if the connection is secured; otherwise, false. + /// + public override bool IsSecureConnection { + get { + return _context.Connection.IsSecure; + } + } + + /// + /// Gets a value indicating whether the request is a WebSocket connection request. + /// + /// + /// true if the request is a WebSocket connection request; otherwise, false. + /// + public override bool IsWebSocketRequest { + get { + return _context.Request.IsWebSocketRequest; + } + } + + /// + /// Gets the value of the Origin header included in the request. + /// + /// + /// A that represents the value of the Origin header. + /// + public override string Origin { + get { + return _context.Request.Headers ["Origin"]; + } + } + + /// + /// Gets the query string included in the request. + /// + /// + /// A that contains the query string parameters. + /// + public override NameValueCollection QueryString { + get { + return _context.Request.QueryString; + } + } + + /// + /// Gets the URI requested by the client. + /// + /// + /// A that represents the requested URI. + /// + public override Uri RequestUri { + get { + return _context.Request.Url; + } + } + + /// + /// Gets the value of the Sec-WebSocket-Key header included in the request. + /// + /// + /// This property provides a part of the information used by the server to prove that it + /// received a valid WebSocket connection request. + /// + /// + /// A that represents the value of the Sec-WebSocket-Key header. + /// + public override string SecWebSocketKey { + get { + return _context.Request.Headers ["Sec-WebSocket-Key"]; + } + } + + /// + /// Gets the values of the Sec-WebSocket-Protocol header included in the request. + /// + /// + /// This property represents the subprotocols requested by the client. + /// + /// + /// An instance that provides + /// an enumerator which supports the iteration over the values of the Sec-WebSocket-Protocol + /// header. + /// + public override IEnumerable SecWebSocketProtocols { + get { + var protocols = _context.Request.Headers ["Sec-WebSocket-Protocol"]; + if (protocols != null) + foreach (var protocol in protocols.Split (',')) + yield return protocol.Trim (); + } + } + + /// + /// Gets the value of the Sec-WebSocket-Version header included in the request. + /// + /// + /// This property represents the WebSocket protocol version. + /// + /// + /// A that represents the value of the Sec-WebSocket-Version header. + /// + public override string SecWebSocketVersion { + get { + return _context.Request.Headers ["Sec-WebSocket-Version"]; + } + } + + /// + /// Gets the server endpoint as an IP address and a port number. + /// + /// + /// A that represents the server endpoint. + /// + public override System.Net.IPEndPoint ServerEndPoint { + get { + return _context.Connection.LocalEndPoint; + } + } + + /// + /// Gets the client information (identity, authentication, and security roles). + /// + /// + /// A that represents the client information. + /// + public override IPrincipal User { + get { + return _context.User; + } + } + + /// + /// Gets the client endpoint as an IP address and a port number. + /// + /// + /// A that represents the client endpoint. + /// + public override System.Net.IPEndPoint UserEndPoint { + get { + return _context.Connection.RemoteEndPoint; + } + } + + /// + /// Gets the instance used for two-way communication + /// between client and server. + /// + /// + /// A . + /// + public override WebSocket WebSocket { + get { + return _websocket; + } + } + + #endregion + + #region Internal Methods + + internal void Close () + { + _context.Connection.Close (true); + } + + internal void Close (HttpStatusCode code) + { + _context.Response.Close (code); + } + + #endregion + + #region Public Methods + + /// + /// Returns a that represents the current + /// . + /// + /// + /// A that represents the current + /// . + /// + public override string ToString () + { + return _context.Request.ToString (); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/WebSockets/TcpListenerWebSocketContext.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/WebSockets/TcpListenerWebSocketContext.cs new file mode 100644 index 0000000..ac7c029 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/WebSockets/TcpListenerWebSocketContext.cs @@ -0,0 +1,388 @@ +#region License +/* + * TcpListenerWebSocketContext.cs + * + * The MIT License + * + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Net.Sockets; +using System.Security.Cryptography.X509Certificates; +using System.Security.Principal; +using System.Text; + +namespace WebSocketSharp.Net.WebSockets +{ + /// + /// Provides the properties used to access the information in a WebSocket connection request + /// received by the . + /// + /// + /// + public class TcpListenerWebSocketContext : WebSocketContext + { + #region Private Fields + + private TcpClient _client; + private CookieCollection _cookies; + private NameValueCollection _queryString; + private HandshakeRequest _request; + private bool _secure; + private WebSocketStream _stream; + private Uri _uri; + private IPrincipal _user; + private WebSocket _websocket; + + #endregion + + #region Internal Constructors + + internal TcpListenerWebSocketContext ( + TcpClient client, string protocol, bool secure, X509Certificate cert, Logger logger) + { + _client = client; + _secure = secure; + _stream = WebSocketStream.CreateServerStream (client, secure, cert); + _request = _stream.ReadHandshake (HandshakeRequest.Parse, 90000); + _uri = HttpUtility.CreateRequestUrl ( + _request.RequestUri, _request.Headers ["Host"], _request.IsWebSocketRequest, secure); + + _websocket = new WebSocket (this, protocol, logger); + } + + #endregion + + #region Internal Properties + + internal WebSocketStream Stream { + get { + return _stream; + } + } + + #endregion + + #region Public Properties + + /// + /// Gets the HTTP cookies included in the request. + /// + /// + /// A that contains the cookies. + /// + public override CookieCollection CookieCollection { + get { + return _cookies ?? (_cookies = _request.Cookies); + } + } + + /// + /// Gets the HTTP headers included in the request. + /// + /// + /// A that contains the headers. + /// + public override NameValueCollection Headers { + get { + return _request.Headers; + } + } + + /// + /// Gets the value of the Host header included in the request. + /// + /// + /// A that represents the value of the Host header. + /// + public override string Host { + get { + return _request.Headers ["Host"]; + } + } + + /// + /// Gets a value indicating whether the client is authenticated. + /// + /// + /// true if the client is authenticated; otherwise, false. + /// + public override bool IsAuthenticated { + get { + return _user != null && _user.Identity.IsAuthenticated; + } + } + + /// + /// Gets a value indicating whether the client connected from the local computer. + /// + /// + /// true if the client connected from the local computer; otherwise, false. + /// + public override bool IsLocal { + get { + return UserEndPoint.Address.IsLocal (); + } + } + + /// + /// Gets a value indicating whether the WebSocket connection is secured. + /// + /// + /// true if the connection is secured; otherwise, false. + /// + public override bool IsSecureConnection { + get { + return _secure; + } + } + + /// + /// Gets a value indicating whether the request is a WebSocket connection request. + /// + /// + /// true if the request is a WebSocket connection request; otherwise, false. + /// + public override bool IsWebSocketRequest { + get { + return _request.IsWebSocketRequest; + } + } + + /// + /// Gets the value of the Origin header included in the request. + /// + /// + /// A that represents the value of the Origin header. + /// + public override string Origin { + get { + return _request.Headers ["Origin"]; + } + } + + /// + /// Gets the query string included in the request. + /// + /// + /// A that contains the query string parameters. + /// + public override NameValueCollection QueryString { + get { + return _queryString ?? + (_queryString = HttpUtility.ParseQueryStringInternally ( + _uri != null ? _uri.Query : null, Encoding.UTF8)); + } + } + + /// + /// Gets the URI requested by the client. + /// + /// + /// A that represents the requested URI. + /// + public override Uri RequestUri { + get { + return _uri; + } + } + + /// + /// Gets the value of the Sec-WebSocket-Key header included in the request. + /// + /// + /// This property provides a part of the information used by the server to prove that it + /// received a valid WebSocket connection request. + /// + /// + /// A that represents the value of the Sec-WebSocket-Key header. + /// + public override string SecWebSocketKey { + get { + return _request.Headers ["Sec-WebSocket-Key"]; + } + } + + /// + /// Gets the values of the Sec-WebSocket-Protocol header included in the request. + /// + /// + /// This property represents the subprotocols requested by the client. + /// + /// + /// An instance that provides + /// an enumerator which supports the iteration over the values of the Sec-WebSocket-Protocol + /// header. + /// + public override IEnumerable SecWebSocketProtocols { + get { + var protocols = _request.Headers ["Sec-WebSocket-Protocol"]; + if (protocols != null) + foreach (var protocol in protocols.Split (',')) + yield return protocol.Trim (); + } + } + + /// + /// Gets the value of the Sec-WebSocket-Version header included in the request. + /// + /// + /// This property represents the WebSocket protocol version. + /// + /// + /// A that represents the value of the Sec-WebSocket-Version header. + /// + public override string SecWebSocketVersion { + get { + return _request.Headers ["Sec-WebSocket-Version"]; + } + } + + /// + /// Gets the server endpoint as an IP address and a port number. + /// + /// + /// A that represents the server endpoint. + /// + public override System.Net.IPEndPoint ServerEndPoint { + get { + return (System.Net.IPEndPoint) _client.Client.LocalEndPoint; + } + } + + /// + /// Gets the client information (identity, authentication, and security roles). + /// + /// + /// A that represents the client information. + /// + public override IPrincipal User { + get { + return _user; + } + } + + /// + /// Gets the client endpoint as an IP address and a port number. + /// + /// + /// A that represents the client endpoint. + /// + public override System.Net.IPEndPoint UserEndPoint { + get { + return (System.Net.IPEndPoint) _client.Client.RemoteEndPoint; + } + } + + /// + /// Gets the instance used for two-way communication + /// between client and server. + /// + /// + /// A . + /// + public override WebSocket WebSocket { + get { + return _websocket; + } + } + + #endregion + + #region Internal Methods + + internal void Close () + { + _stream.Close (); + _client.Close (); + } + + internal void Close (HttpStatusCode code) + { + _websocket.Close (HandshakeResponse.CreateCloseResponse (code)); + } + + internal void SendAuthChallenge (string challenge) + { + var res = new HandshakeResponse (HttpStatusCode.Unauthorized); + res.Headers ["WWW-Authenticate"] = challenge; + _stream.WriteHandshake (res); + _request = _stream.ReadHandshake (HandshakeRequest.Parse, 15000); + } + + internal void SetUser ( + AuthenticationSchemes scheme, + string realm, + Func credentialsFinder) + { + var authRes = _request.AuthResponse; + if (authRes == null) + return; + + var id = authRes.ToIdentity (); + if (id == null) + return; + + NetworkCredential cred = null; + try { + cred = credentialsFinder (id); + } + catch { + } + + if (cred == null) + return; + + var valid = scheme == AuthenticationSchemes.Basic + ? ((HttpBasicIdentity) id).Password == cred.Password + : scheme == AuthenticationSchemes.Digest + ? ((HttpDigestIdentity) id).IsValid ( + cred.Password, realm, _request.HttpMethod, null) + : false; + + if (valid) + _user = new GenericPrincipal (id, cred.Roles); + } + + #endregion + + #region Public Methods + + /// + /// Returns a that represents the current + /// . + /// + /// + /// A that represents the current + /// . + /// + public override string ToString () + { + return _request.ToString (); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/WebSockets/WebSocketContext.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/WebSockets/WebSocketContext.cs new file mode 100644 index 0000000..c6424d8 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Net/WebSockets/WebSocketContext.cs @@ -0,0 +1,208 @@ +#region License +/* + * WebSocketContext.cs + * + * The MIT License + * + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Security.Principal; + +namespace WebSocketSharp.Net.WebSockets +{ + /// + /// Exposes the properties used to access the information in a WebSocket connection request. + /// + /// + /// The WebSocketContext class is an abstract class. + /// + public abstract class WebSocketContext + { + #region Protected Constructors + + /// + /// Initializes a new instance of the class. + /// + protected WebSocketContext () + { + } + + #endregion + + #region Public Properties + + /// + /// Gets the HTTP cookies included in the request. + /// + /// + /// A that contains the cookies. + /// + public abstract CookieCollection CookieCollection { get; } + + /// + /// Gets the HTTP headers included in the request. + /// + /// + /// A that contains the headers. + /// + public abstract NameValueCollection Headers { get; } + + /// + /// Gets the value of the Host header included in the request. + /// + /// + /// A that represents the value of the Host header. + /// + public abstract string Host { get; } + + /// + /// Gets a value indicating whether the client is authenticated. + /// + /// + /// true if the client is authenticated; otherwise, false. + /// + public abstract bool IsAuthenticated { get; } + + /// + /// Gets a value indicating whether the client connected from the local computer. + /// + /// + /// true if the client connected from the local computer; otherwise, false. + /// + public abstract bool IsLocal { get; } + + /// + /// Gets a value indicating whether the WebSocket connection is secured. + /// + /// + /// true if the connection is secured; otherwise, false. + /// + public abstract bool IsSecureConnection { get; } + + /// + /// Gets a value indicating whether the request is a WebSocket connection request. + /// + /// + /// true if the request is a WebSocket connection request; otherwise, false. + /// + public abstract bool IsWebSocketRequest { get; } + + /// + /// Gets the value of the Origin header included in the request. + /// + /// + /// A that represents the value of the Origin header. + /// + public abstract string Origin { get; } + + /// + /// Gets the query string included in the request. + /// + /// + /// A that contains the query string parameters. + /// + public abstract NameValueCollection QueryString { get; } + + /// + /// Gets the URI requested by the client. + /// + /// + /// A that represents the requested URI. + /// + public abstract Uri RequestUri { get; } + + /// + /// Gets the value of the Sec-WebSocket-Key header included in the request. + /// + /// + /// This property provides a part of the information used by the server to prove that it + /// received a valid WebSocket connection request. + /// + /// + /// A that represents the value of the Sec-WebSocket-Key header. + /// + public abstract string SecWebSocketKey { get; } + + /// + /// Gets the values of the Sec-WebSocket-Protocol header included in the request. + /// + /// + /// This property represents the subprotocols requested by the client. + /// + /// + /// An instance that provides + /// an enumerator which supports the iteration over the values of the Sec-WebSocket-Protocol + /// header. + /// + public abstract IEnumerable SecWebSocketProtocols { get; } + + /// + /// Gets the value of the Sec-WebSocket-Version header included in the request. + /// + /// + /// This property represents the WebSocket protocol version. + /// + /// + /// A that represents the value of the Sec-WebSocket-Version header. + /// + public abstract string SecWebSocketVersion { get; } + + /// + /// Gets the server endpoint as an IP address and a port number. + /// + /// + /// A that represents the server endpoint. + /// + public abstract System.Net.IPEndPoint ServerEndPoint { get; } + + /// + /// Gets the client information (identity, authentication, and security roles). + /// + /// + /// A that represents the client information. + /// + public abstract IPrincipal User { get; } + + /// + /// Gets the client endpoint as an IP address and a port number. + /// + /// + /// A that represents the client endpoint. + /// + public abstract System.Net.IPEndPoint UserEndPoint { get; } + + /// + /// Gets the instance used for two-way communication + /// between client and server. + /// + /// + /// A . + /// + public abstract WebSocket WebSocket { get; } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Opcode.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Opcode.cs new file mode 100644 index 0000000..157395d --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Opcode.cs @@ -0,0 +1,73 @@ +#region License +/* + * Opcode.cs + * + * The MIT License + * + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; + +namespace WebSocketSharp +{ + /// + /// Contains the values of the opcode that indicates the type of a WebSocket frame. + /// + /// + /// The values of the opcode are defined in + /// Section 5.2 of RFC 6455. + /// + public enum Opcode : byte + { + /// + /// Equivalent to numeric value 0. + /// Indicates a continuation frame. + /// + Cont = 0x0, + /// + /// Equivalent to numeric value 1. + /// Indicates a text frame. + /// + Text = 0x1, + /// + /// Equivalent to numeric value 2. + /// Indicates a binary frame. + /// + Binary = 0x2, + /// + /// Equivalent to numeric value 8. + /// Indicates a connection close frame. + /// + Close = 0x8, + /// + /// Equivalent to numeric value 9. + /// Indicates a ping frame. + /// + Ping = 0x9, + /// + /// Equivalent to numeric value 10. + /// Indicates a pong frame. + /// + Pong = 0xa + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/PayloadData.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/PayloadData.cs new file mode 100644 index 0000000..1e439e7 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/PayloadData.cs @@ -0,0 +1,181 @@ +#region License +/* + * PayloadData.cs + * + * The MIT License + * + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +namespace WebSocketSharp +{ + internal class PayloadData : IEnumerable + { + #region Private Fields + + private byte [] _applicationData; + private byte [] _extensionData; + private bool _masked; + + #endregion + + #region Public Const Fields + + public const ulong MaxLength = long.MaxValue; + + #endregion + + #region Public Constructors + + public PayloadData () + : this (new byte [0], new byte [0], false) + { + } + + public PayloadData (byte [] applicationData) + : this (new byte [0], applicationData, false) + { + } + + public PayloadData (string applicationData) + : this (new byte [0], Encoding.UTF8.GetBytes (applicationData), false) + { + } + + public PayloadData (byte [] applicationData, bool masked) + : this (new byte [0], applicationData, masked) + { + } + + public PayloadData (byte [] extensionData, byte [] applicationData, bool masked) + { + if ((ulong) extensionData.LongLength + (ulong) applicationData.LongLength > MaxLength) + throw new ArgumentOutOfRangeException ( + "The length of 'extensionData' plus 'applicationData' is greater than MaxLength."); + + _extensionData = extensionData; + _applicationData = applicationData; + _masked = masked; + } + + #endregion + + #region Internal Properties + + internal bool ContainsReservedCloseStatusCode { + get { + return _applicationData.Length > 1 && + _applicationData.SubArray (0, 2).ToUInt16 (ByteOrder.Big).IsReserved (); + } + } + + #endregion + + #region Public Properties + + public byte [] ApplicationData { + get { + return _applicationData; + } + } + + public byte [] ExtensionData { + get { + return _extensionData; + } + } + + public bool IsMasked { + get { + return _masked; + } + } + + public ulong Length { + get { + return (ulong) (_extensionData.LongLength + _applicationData.LongLength); + } + } + + #endregion + + #region Private Methods + + private static void mask (byte [] src, byte [] key) + { + for (long i = 0; i < src.LongLength; i++) + src [i] = (byte) (src [i] ^ key [i % 4]); + } + + #endregion + + #region Public Methods + + public IEnumerator GetEnumerator () + { + foreach (byte b in _extensionData) + yield return b; + + foreach (byte b in _applicationData) + yield return b; + } + + public void Mask (byte [] maskingKey) + { + if (_extensionData.LongLength > 0) + mask (_extensionData, maskingKey); + + if (_applicationData.LongLength > 0) + mask (_applicationData, maskingKey); + + _masked = !_masked; + } + + public byte [] ToByteArray () + { + return _extensionData.LongLength > 0 + ? new List (this).ToArray () + : _applicationData; + } + + public override string ToString () + { + return BitConverter.ToString (ToByteArray ()); + } + + #endregion + + #region Explicitly Implemented Interface Members + + IEnumerator IEnumerable.GetEnumerator () + { + return GetEnumerator (); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Rsv.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Rsv.cs new file mode 100644 index 0000000..993ea91 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/Rsv.cs @@ -0,0 +1,38 @@ +#region License +/* + * Rsv.cs + * + * The MIT License + * + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; + +namespace WebSocketSharp +{ + internal enum Rsv : byte + { + Off = 0x0, + On = 0x1 + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/WebSocket.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/WebSocket.cs new file mode 100644 index 0000000..e12323d --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/WebSocket.cs @@ -0,0 +1,2188 @@ +#region License +/* + * WebSocket.cs + * + * A C# implementation of the WebSocket interface. + * + * This code is derived from WebSocket.java + * (http://github.com/adamac/Java-WebSocket-client). + * + * The MIT License + * + * Copyright (c) 2009 Adam MacBeth + * Copyright (c) 2010-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Diagnostics; +using System.IO; +using System.Net.Sockets; +using System.Net.Security; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using WebSocketSharp.Net; +using WebSocketSharp.Net.WebSockets; + +namespace WebSocketSharp +{ + /// + /// Implements the WebSocket interface. + /// + /// + /// The WebSocket class provides a set of methods and properties for two-way communication using + /// the WebSocket protocol (RFC 6455). + /// + public class WebSocket : IDisposable + { + #region Private Const Fields + + private const string _guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + private const string _version = "13"; + + #endregion + + #region Private Fields + + private AuthenticationChallenge _authChallenge; + private string _base64Key; + private RemoteCertificateValidationCallback + _certValidationCallback; + private bool _client; + private Action _closeContext; + private CompressionMethod _compression; + private WebSocketContext _context; + private CookieCollection _cookies; + private NetworkCredential _credentials; + private string _extensions; + private AutoResetEvent _exitReceiving; + private object _forConn; + private object _forEvent; + private object _forMessageEventQueue; + private object _forSend; + private Func + _handshakeRequestChecker; + private volatile Logger _logger; + private Queue _messageEventQueue; + private uint _nonceCount; + private string _origin; + private NameValueCollection _customHeaders; + private bool _preAuth; + private string _protocol; + private string [] _protocols; + private volatile WebSocketState _readyState; + private AutoResetEvent _receivePong; + private bool _secure; + private WebSocketStream _stream; + private TcpClient _tcpClient; + private Uri _uri; + + #endregion + + #region Internal Const Fields + + internal const int FragmentLength = 1016; // Max value is int.MaxValue - 14. + + #endregion + + #region Internal Constructors + + // As server + internal WebSocket (HttpListenerWebSocketContext context, string protocol, Logger logger) + { + _context = context; + _protocol = protocol; + _logger = logger; + + _closeContext = context.Close; + _secure = context.IsSecureConnection; + _stream = context.Stream; + + init (); + } + + // As server + internal WebSocket (TcpListenerWebSocketContext context, string protocol, Logger logger) + { + _context = context; + _protocol = protocol; + _logger = logger; + + _closeContext = context.Close; + _secure = context.IsSecureConnection; + _stream = context.Stream; + + init (); + } + + #endregion + + #region Public Constructors + + /// + /// Initializes a new instance of the class with the specified + /// WebSocket URL and subprotocols. + /// + /// + /// A that represents the WebSocket URL to connect. + /// + /// + /// An array of that contains the WebSocket subprotocols if any. + /// Each value of must be a token defined in + /// RFC 2616. + /// + /// + /// + /// is invalid. + /// + /// + /// -or- + /// + /// + /// is invalid. + /// + /// + /// + /// is . + /// + public WebSocket (string url, params string [] protocols) + { + if (url == null) + throw new ArgumentNullException ("url"); + + string msg; + if (!url.TryCreateWebSocketUri (out _uri, out msg)) + throw new ArgumentException (msg, "url"); + + if (protocols != null && protocols.Length > 0) { + msg = protocols.CheckIfValidProtocols (); + if (msg != null) + throw new ArgumentException (msg, "protocols"); + + _protocols = protocols; + } + + _base64Key = CreateBase64Key (); + _client = true; + _logger = new Logger (); + _secure = _uri.Scheme == "wss"; + + init (); + } + + #endregion + + #region Internal Properties + + internal CookieCollection CookieCollection { + get { + return _cookies; + } + } + + // As server + internal Func CustomHandshakeRequestChecker { + get { + return _handshakeRequestChecker ?? (context => null); + } + + set { + _handshakeRequestChecker = value; + } + } + + internal bool IsConnected { + get { + return _readyState == WebSocketState.Open || _readyState == WebSocketState.Closing; + } + } + + #endregion + + #region Public Properties + + /// + /// Gets or sets the compression method used to compress the message on the WebSocket + /// connection. + /// + /// + /// One of the enum values, indicates the compression method + /// used to compress the message. The default value is . + /// + public CompressionMethod Compression { + get { + return _compression; + } + + set { + lock (_forConn) { + var msg = checkIfAvailable ("Set operation of Compression", false, false); + if (msg != null) { + _logger.Error (msg); + error (msg); + + return; + } + + _compression = value; + } + } + } + + /// + /// Gets the HTTP cookies included in the WebSocket connection request and response. + /// + /// + /// An IEnumerable<Cookie> instance that provides an enumerator which supports the + /// iteration over the collection of the cookies. + /// + public IEnumerable Cookies { + get { + lock (_cookies.SyncRoot) { + foreach (Cookie cookie in _cookies) + yield return cookie; + } + } + } + + /// + /// Gets the credentials for the HTTP authentication (Basic/Digest). + /// + /// + /// A that represents the credentials for the HTTP + /// authentication. The default value is . + /// + public NetworkCredential Credentials { + get { + return _credentials; + } + } + + /// + /// Gets the WebSocket extensions selected by the server. + /// + /// + /// A that represents the extensions if any. The default value is + /// . + /// + public string Extensions { + get { + return _extensions ?? String.Empty; + } + } + + /// + /// Gets a value indicating whether the WebSocket connection is alive. + /// + /// + /// true if the connection is alive; otherwise, false. + /// + public bool IsAlive { + get { + return Ping (); + } + } + + /// + /// Gets a value indicating whether the WebSocket connection is secure. + /// + /// + /// true if the connection is secure; otherwise, false. + /// + public bool IsSecure { + get { + return _secure; + } + } + + /// + /// Gets the logging functions. + /// + /// + /// The default logging level is . If you would like to change it, + /// you should set the Log.Level property to any of the enum + /// values. + /// + /// + /// A that provides the logging functions. + /// + public Logger Log { + get { + return _logger; + } + + internal set { + _logger = value; + } + } + + /// + /// Gets or sets the value of the Origin header to send with the WebSocket connection request + /// to the server. + /// + /// + /// The sends the Origin header if this property has any. + /// + /// + /// + /// A that represents the value of the + /// HTTP Origin + /// header to send. The default value is . + /// + /// + /// The Origin header has the following syntax: + /// <scheme>://<host>[:<port>] + /// + /// + public string Origin { + get { + return _origin; + } + + set { + lock (_forConn) { + var msg = checkIfAvailable ("Set operation of Origin", false, false); + if (msg == null) { + if (value.IsNullOrEmpty ()) { + _origin = value; + return; + } + + Uri origin; + if (!Uri.TryCreate (value, UriKind.Absolute, out origin) || origin.Segments.Length > 1) + msg = "The syntax of Origin must be '://[:]'."; + } + + if (msg != null) { + _logger.Error (msg); + error (msg); + + return; + } + + _origin = value.TrimEnd ('/'); + } + } + } + + /// + /// Gets the WebSocket subprotocol selected by the server. + /// + /// + /// A that represents the subprotocol if any. The default value is + /// . + /// + public string Protocol { + get { + return _protocol ?? String.Empty; + } + + internal set { + _protocol = value; + } + } + + /// + /// Gets the state of the WebSocket connection. + /// + /// + /// One of the enum values, indicates the state of the WebSocket + /// connection. The default value is . + /// + public WebSocketState ReadyState { + get { + return _readyState; + } + } + + /// + /// Gets or sets the callback used to validate the certificate supplied by the server. + /// + /// + /// If the value of this property is , the validation does nothing with + /// the server certificate, always returns valid. + /// + /// + /// A delegate that references the method(s) + /// used to validate the server certificate. The default value is . + /// + public RemoteCertificateValidationCallback ServerCertificateValidationCallback { + get { + return _certValidationCallback; + } + + set { + lock (_forConn) { + var msg = checkIfAvailable ( + "Set operation of ServerCertificateValidationCallback", false, false); + + if (msg != null) { + _logger.Error (msg); + error (msg); + + return; + } + + _certValidationCallback = value; + } + } + } + + /// + /// Gets the WebSocket URL to connect. + /// + /// + /// A that represents the WebSocket URL to connect. + /// + public Uri Url { + get { + return _client + ? _uri + : _context.RequestUri; + } + } + + #endregion + + #region Public Events + + /// + /// Occurs when the WebSocket connection has been closed. + /// + public event EventHandler OnClose; + + /// + /// Occurs when the gets an error. + /// + public event EventHandler OnError; + + /// + /// Occurs when the receives a message. + /// + public event EventHandler OnMessage; + + /// + /// Occurs when the WebSocket connection has been established. + /// + public event EventHandler OnOpen; + + #endregion + + #region Private Methods + + private bool acceptCloseFrame (WebSocketFrame frame) + { + var payload = frame.PayloadData; + close (payload, !payload.ContainsReservedCloseStatusCode, false); + + return false; + } + + private bool acceptDataFrame (WebSocketFrame frame) + { + var e = frame.IsCompressed + ? new MessageEventArgs ( + frame.Opcode, frame.PayloadData.ApplicationData.Decompress (_compression)) + : new MessageEventArgs (frame.Opcode, frame.PayloadData); + + enqueueToMessageEventQueue (e); + return true; + } + + private void acceptException (Exception exception, string message) + { + var code = CloseStatusCode.Abnormal; + var reason = message; + if (exception is WebSocketException) { + var wsex = (WebSocketException) exception; + code = wsex.Code; + reason = wsex.Message; + } + + if (code == CloseStatusCode.Abnormal || code == CloseStatusCode.TlsHandshakeFailure) + _logger.Fatal (exception.ToString ()); + else + _logger.Error (reason); + + error (message ?? code.GetMessage ()); + if (_readyState == WebSocketState.Connecting && !_client) + Close (HttpStatusCode.BadRequest); + else + close (code, reason ?? code.GetMessage (), false); + } + + private bool acceptFragmentedFrame (WebSocketFrame frame) + { + return frame.IsContinuation // Not first fragment + ? true + : acceptFragments (frame); + } + + private bool acceptFragments (WebSocketFrame first) + { + using (var concatenated = new MemoryStream ()) { + concatenated.WriteBytes (first.PayloadData.ApplicationData); + if (!concatenateFragmentsInto (concatenated)) + return false; + + byte [] data; + if (_compression != CompressionMethod.None) { + data = concatenated.DecompressToArray (_compression); + } + else { + concatenated.Close (); + data = concatenated.ToArray (); + } + + enqueueToMessageEventQueue (new MessageEventArgs (first.Opcode, data)); + return true; + } + } + + private bool acceptFrame (WebSocketFrame frame) + { + return frame.IsCompressed && _compression == CompressionMethod.None + ? acceptUnsupportedFrame ( + frame, + CloseStatusCode.IncorrectData, + "A compressed data has been received without available decompression method.") + : frame.IsFragmented + ? acceptFragmentedFrame (frame) + : frame.IsData + ? acceptDataFrame (frame) + : frame.IsPing + ? acceptPingFrame (frame) + : frame.IsPong + ? acceptPongFrame (frame) + : frame.IsClose + ? acceptCloseFrame (frame) + : acceptUnsupportedFrame (frame, CloseStatusCode.PolicyViolation, null); + } + + // As server + private bool acceptHandshake () + { + _logger.Debug ( + String.Format ( + "A WebSocket connection request from {0}:\n{1}", _context.UserEndPoint, _context)); + + var msg = checkIfValidHandshakeRequest (_context); + if (msg != null) { + _logger.Error (msg); + error ("An error has occurred while connecting."); + Close (HttpStatusCode.BadRequest); + + return false; + } + + if (_protocol != null && + !_context.SecWebSocketProtocols.Contains (protocol => protocol == _protocol)) + _protocol = null; + + var extensions = _context.Headers ["Sec-WebSocket-Extensions"]; + if (extensions != null && extensions.Length > 0) + acceptSecWebSocketExtensionsHeader (extensions); + + return send (createHandshakeResponse ()); + } + + private bool acceptPingFrame (WebSocketFrame frame) + { + var mask = _client ? Mask.Mask : Mask.Unmask; + if (send (WebSocketFrame.CreatePongFrame (mask, frame.PayloadData))) + _logger.Trace ("Returned a Pong."); + + return true; + } + + private bool acceptPongFrame (WebSocketFrame frame) + { + _receivePong.Set (); + _logger.Trace ("Received a Pong."); + + return true; + } + + // As server + private void acceptSecWebSocketExtensionsHeader (string value) + { + var extensions = new StringBuilder (32); + + var compress = false; + foreach (var extension in value.SplitHeaderValue (',')) { + var trimed = extension.Trim (); + var unprefixed = trimed.RemovePrefix ("x-webkit-"); + + if (!compress && unprefixed.IsCompressionExtension ()) { + var method = unprefixed.ToCompressionMethod (); + if (method != CompressionMethod.None) { + _compression = method; + compress = true; + + extensions.Append (trimed + ", "); + } + } + } + + var len = extensions.Length; + if (len > 0) { + extensions.Length = len - 2; + _extensions = extensions.ToString (); + } + } + + private bool acceptUnsupportedFrame (WebSocketFrame frame, CloseStatusCode code, string reason) + { + _logger.Debug ("Unsupported frame:\n" + frame.PrintToString (false)); + acceptException (new WebSocketException (code, reason), null); + + return false; + } + + private string checkIfAvailable ( + string operation, bool availableAsServer, bool availableAsConnected) + { + return !_client && !availableAsServer + ? operation + " isn't available as a server." + : !availableAsConnected + ? _readyState.CheckIfConnectable () + : null; + } + + private string checkIfCanConnect () + { + return !_client && _readyState == WebSocketState.Closed + ? "Connect isn't available to reconnect as a server." + : _readyState.CheckIfConnectable (); + } + + // As server + private string checkIfValidHandshakeRequest (WebSocketContext context) + { + var headers = context.Headers; + return context.RequestUri == null + ? "Invalid request url." + : !context.IsWebSocketRequest + ? "Not WebSocket connection request." + : !validateSecWebSocketKeyHeader (headers ["Sec-WebSocket-Key"]) + ? "Invalid Sec-WebSocket-Key header." + : !validateSecWebSocketVersionClientHeader (headers ["Sec-WebSocket-Version"]) + ? "Invalid Sec-WebSocket-Version header." + : CustomHandshakeRequestChecker (context); + } + + // As client + private string checkIfValidHandshakeResponse (HandshakeResponse response) + { + var headers = response.Headers; + return response.IsUnauthorized + ? String.Format ("HTTP {0} authorization is required.", response.AuthChallenge.Scheme) + : !response.IsWebSocketResponse + ? "Not WebSocket connection response." + : !validateSecWebSocketAcceptHeader (headers ["Sec-WebSocket-Accept"]) + ? "Invalid Sec-WebSocket-Accept header." + : !validateSecWebSocketProtocolHeader (headers ["Sec-WebSocket-Protocol"]) + ? "Invalid Sec-WebSocket-Protocol header." + : !validateSecWebSocketExtensionsHeader (headers ["Sec-WebSocket-Extensions"]) + ? "Invalid Sec-WebSocket-Extensions header." + : !validateSecWebSocketVersionServerHeader (headers ["Sec-WebSocket-Version"]) + ? "Invalid Sec-WebSocket-Version header." + : null; + } + + private void close (CloseStatusCode code, string reason, bool wait) + { + close (new PayloadData (((ushort) code).Append (reason)), !code.IsReserved (), wait); + } + + private void close (PayloadData payload, bool send, bool wait) + { + lock (_forConn) { + if (_readyState == WebSocketState.Closing || _readyState == WebSocketState.Closed) { + _logger.Info ("Closing the WebSocket connection has already been done."); + return; + } + + _readyState = WebSocketState.Closing; + } + + _logger.Trace ("Start closing handshake."); + + var e = new CloseEventArgs (payload); + e.WasClean = + _client + ? closeHandshake ( + send ? WebSocketFrame.CreateCloseFrame (Mask.Mask, payload).ToByteArray () : null, + wait ? 5000 : 0, + closeClientResources) + : closeHandshake ( + send ? WebSocketFrame.CreateCloseFrame (Mask.Unmask, payload).ToByteArray () : null, + wait ? 1000 : 0, + closeServerResources); + + _logger.Trace ("End closing handshake."); + + _readyState = WebSocketState.Closed; + try { + OnClose.Emit (this, e); + } + catch (Exception ex) { + _logger.Fatal (ex.ToString ()); + error ("An exception has occurred while OnClose."); + } + } + + private void closeAsync (PayloadData payload, bool send, bool wait) + { + Action closer = close; + closer.BeginInvoke (payload, send, wait, ar => closer.EndInvoke (ar), null); + } + + // As client + private void closeClientResources () + { + if (_stream != null) { + _stream.Dispose (); + _stream = null; + } + + if (_tcpClient != null) { + _tcpClient.Close (); + _tcpClient = null; + } + } + + private bool closeHandshake (byte [] frame, int timeout, Action release) + { + var sent = frame != null && _stream.Write (frame); + var received = timeout == 0 || + (sent && _exitReceiving != null && _exitReceiving.WaitOne (timeout)); + + release (); + if (_receivePong != null) { + _receivePong.Close (); + _receivePong = null; + } + + if (_exitReceiving != null) { + _exitReceiving.Close (); + _exitReceiving = null; + } + + var result = sent && received; + _logger.Debug ( + String.Format ("Was clean?: {0}\nsent: {1} received: {2}", result, sent, received)); + + return result; + } + + // As server + private void closeServerResources () + { + if (_closeContext == null) + return; + + _closeContext (); + _closeContext = null; + _stream = null; + _context = null; + } + + private bool concatenateFragmentsInto (Stream dest) + { + while (true) { + var frame = _stream.ReadFrame (); + + if (frame.IsFinal) { + // FINAL + + // CONT + if (frame.IsContinuation) { + dest.WriteBytes (frame.PayloadData.ApplicationData); + break; + } + + // PING + if (frame.IsPing) { + acceptPingFrame (frame); + continue; + } + + // PONG + if (frame.IsPong) { + acceptPongFrame (frame); + continue; + } + + // CLOSE + if (frame.IsClose) + return acceptCloseFrame (frame); + } + else { + // MORE + + // CONT + if (frame.IsContinuation) { + dest.WriteBytes (frame.PayloadData.ApplicationData); + continue; + } + } + + // ? + return acceptUnsupportedFrame ( + frame, + CloseStatusCode.IncorrectData, + "An incorrect data has been received while receiving fragmented data."); + } + + return true; + } + + private bool connect () + { + lock (_forConn) { + var msg = _readyState.CheckIfConnectable (); + if (msg != null) { + _logger.Error (msg); + error (msg); + + return false; + } + + try { + if (_client ? doHandshake () : acceptHandshake ()) { + _readyState = WebSocketState.Open; + return true; + } + } + catch (Exception ex) { + acceptException (ex, "An exception has occurred while connecting."); + } + + return false; + } + } + + // As client + private string createExtensionsRequest () + { + var extensions = new StringBuilder (32); + + if (_compression != CompressionMethod.None) + extensions.Append (_compression.ToExtensionString ()); + + return extensions.Length > 0 + ? extensions.ToString () + : null; + } + + // As client + private HandshakeRequest createHandshakeRequest () + { + var path = _uri.PathAndQuery; + var host = _uri.Port == 80 ? _uri.DnsSafeHost : _uri.Authority; + + var req = new HandshakeRequest (path); + var headers = req.Headers; + + headers ["Host"] = host; + + if (!_origin.IsNullOrEmpty ()) + headers ["Origin"] = _origin; + + headers ["Sec-WebSocket-Key"] = _base64Key; + + if (_protocols != null) + headers ["Sec-WebSocket-Protocol"] = _protocols.ToString (", "); + + var extensions = createExtensionsRequest (); + if (extensions != null) + headers ["Sec-WebSocket-Extensions"] = extensions; + + headers ["Sec-WebSocket-Version"] = _version; + + AuthenticationResponse authRes = null; + if (_authChallenge != null && _credentials != null) { + authRes = new AuthenticationResponse (_authChallenge, _credentials, _nonceCount); + _nonceCount = authRes.NonceCount; + } + else if (_preAuth) + authRes = new AuthenticationResponse (_credentials); + + if (authRes != null) + headers ["Authorization"] = authRes.ToString (); + + // add custom headers + if (_customHeaders != null) { + headers.Add(_customHeaders); + } + + + if (_cookies.Count > 0) + req.SetCookies (_cookies); + + return req; + } + + // As server + private HandshakeResponse createHandshakeResponse () + { + var res = new HandshakeResponse (HttpStatusCode.SwitchingProtocols); + var headers = res.Headers; + + headers ["Sec-WebSocket-Accept"] = CreateResponseKey (_base64Key); + + if (_protocol != null) + headers ["Sec-WebSocket-Protocol"] = _protocol; + + if (_extensions != null) + headers ["Sec-WebSocket-Extensions"] = _extensions; + + if (_cookies.Count > 0) + res.SetCookies (_cookies); + + return res; + } + + // As server + private HandshakeResponse createHandshakeResponse (HttpStatusCode code) + { + var res = HandshakeResponse.CreateCloseResponse (code); + res.Headers ["Sec-WebSocket-Version"] = _version; + + return res; + } + + private MessageEventArgs dequeueFromMessageEventQueue () + { + lock (_forMessageEventQueue) + return _messageEventQueue.Count > 0 + ? _messageEventQueue.Dequeue () + : null; + } + + // As client + private bool doHandshake () + { + setClientStream (); + var res = sendHandshakeRequest (); + var msg = checkIfValidHandshakeResponse (res); + if (msg != null) { + _logger.Error (msg); + + msg = "An error has occurred while connecting."; + error (msg); + close (CloseStatusCode.Abnormal, msg, false); + + return false; + } + + var cookies = res.Cookies; + if (cookies.Count > 0) + _cookies.SetOrRemove (cookies); + + return true; + } + + private void enqueueToMessageEventQueue (MessageEventArgs e) + { + lock (_forMessageEventQueue) + _messageEventQueue.Enqueue (e); + } + + private void error (string message) + { + try { + OnError.Emit (this, new ErrorEventArgs (message)); + } + catch (Exception ex) { + _logger.Fatal ("An exception has occurred while OnError:\n" + ex.ToString ()); + } + } + + private void init () + { + _compression = CompressionMethod.None; + _cookies = new CookieCollection (); + _forConn = new object (); + _forEvent = new object (); + _forSend = new object (); + _messageEventQueue = new Queue (); + _forMessageEventQueue = ((ICollection) _messageEventQueue).SyncRoot; + _readyState = WebSocketState.Connecting; + } + + private void open () + { + try { + startReceiving (); + + lock (_forEvent) { + try { + OnOpen.Emit (this, EventArgs.Empty); + } + catch (Exception ex) { + acceptException (ex, "An exception has occurred while OnOpen."); + } + } + } + catch (Exception ex) { + acceptException (ex, "An exception has occurred while opening."); + } + } + + // As client + private HandshakeResponse receiveHandshakeResponse () + { + var res = _stream.ReadHandshakeResponse (); + _logger.Debug ("A response to this WebSocket connection request:\n" + res.ToString ()); + + return res; + } + + private bool send (byte [] frame) + { + lock (_forConn) { + if (_readyState != WebSocketState.Open) { + _logger.Warn ("Sending has been interrupted."); + return false; + } + + return _stream.Write (frame); + } + } + + // As client + private void send (HandshakeRequest request) + { + _logger.Debug ( + String.Format ("A WebSocket connection request to {0}:\n{1}", _uri, request)); + + _stream.WriteHandshake (request); + } + + // As server + private bool send (HandshakeResponse response) + { + _logger.Debug ( + "A response to the WebSocket connection request:\n" + response.ToString ()); + + return _stream.WriteHandshake (response); + } + + private bool send (WebSocketFrame frame) + { + lock (_forConn) { + if (_readyState != WebSocketState.Open) { + _logger.Warn ("Sending has been interrupted."); + return false; + } + + return _stream.Write (frame.ToByteArray ()); + } + } + + private bool send (Opcode opcode, byte [] data) + { + lock (_forSend) { + var sent = false; + try { + var compressed = false; + if (_compression != CompressionMethod.None) { + data = data.Compress (_compression); + compressed = true; + } + + var mask = _client ? Mask.Mask : Mask.Unmask; + sent = send (WebSocketFrame.CreateFrame (Fin.Final, opcode, mask, data, compressed)); + } + catch (Exception ex) { + _logger.Fatal (ex.ToString ()); + error ("An exception has occurred while sending a data."); + } + + return sent; + } + } + + private bool send (Opcode opcode, Stream stream) + { + lock (_forSend) { + var sent = false; + var src = stream; + var compressed = false; + try { + if (_compression != CompressionMethod.None) { + stream = stream.Compress (_compression); + compressed = true; + } + + var mask = _client ? Mask.Mask : Mask.Unmask; + sent = sendFragmented (opcode, stream, mask, compressed); + } + catch (Exception ex) { + _logger.Fatal (ex.ToString ()); + error ("An exception has occurred while sending a data."); + } + finally { + if (compressed) + stream.Dispose (); + + src.Dispose (); + } + + return sent; + } + } + + private void sendAsync (Opcode opcode, byte [] data, Action completed) + { + Func sender = send; + sender.BeginInvoke ( + opcode, + data, + ar => { + try { + var sent = sender.EndInvoke (ar); + if (completed != null) + completed (sent); + } + catch (Exception ex) { + _logger.Fatal (ex.ToString ()); + error ("An exception has occurred while callback."); + } + }, + null); + } + + private void sendAsync (Opcode opcode, Stream stream, Action completed) + { + Func sender = send; + sender.BeginInvoke ( + opcode, + stream, + ar => { + try { + var sent = sender.EndInvoke (ar); + if (completed != null) + completed (sent); + } + catch (Exception ex) { + _logger.Fatal (ex.ToString ()); + error ("An exception has occurred while callback."); + } + }, + null); + } + + private bool sendFragmented (Opcode opcode, Stream stream, Mask mask, bool compressed) + { + var len = stream.Length; + var quo = len / FragmentLength; + var rem = (int) (len % FragmentLength); + var times = rem == 0 ? quo - 2 : quo - 1; + + byte [] buffer = null; + + // Not fragmented + if (quo == 0) { + buffer = new byte [rem]; + return stream.Read (buffer, 0, rem) == rem && + send (WebSocketFrame.CreateFrame (Fin.Final, opcode, mask, buffer, compressed)); + } + + buffer = new byte [FragmentLength]; + + // First + if (stream.Read (buffer, 0, FragmentLength) != FragmentLength || + !send (WebSocketFrame.CreateFrame (Fin.More, opcode, mask, buffer, compressed))) + return false; + + // Mid + for (long i = 0; i < times; i++) { + if (stream.Read (buffer, 0, FragmentLength) != FragmentLength || + !send (WebSocketFrame.CreateFrame (Fin.More, Opcode.Cont, mask, buffer, compressed))) + return false; + } + + // Final + var tmpLen = FragmentLength; + if (rem != 0) + buffer = new byte [tmpLen = rem]; + + return stream.Read (buffer, 0, tmpLen) == tmpLen && + send (WebSocketFrame.CreateFrame (Fin.Final, Opcode.Cont, mask, buffer, compressed)); + } + + // As client + private HandshakeResponse sendHandshakeRequest () + { + var req = createHandshakeRequest (); + var res = sendHandshakeRequest (req); + if (res.IsUnauthorized) { + _authChallenge = res.AuthChallenge; + if (_credentials != null && + (!_preAuth || _authChallenge.Scheme == AuthenticationSchemes.Digest)) { + if (res.Headers.Contains ("Connection", "close")) { + closeClientResources (); + setClientStream (); + } + + var authRes = new AuthenticationResponse (_authChallenge, _credentials, _nonceCount); + _nonceCount = authRes.NonceCount; + req.Headers ["Authorization"] = authRes.ToString (); + res = sendHandshakeRequest (req); + } + } + + return res; + } + + // As client + private HandshakeResponse sendHandshakeRequest (HandshakeRequest request) + { + send (request); + return receiveHandshakeResponse (); + } + + // As client + private void setClientStream () + { + var host = _uri.DnsSafeHost; + var port = _uri.Port; + + _tcpClient = new TcpClient (host, port); + _stream = WebSocketStream.CreateClientStream ( + _tcpClient, _secure, host, _certValidationCallback); + } + + private void startReceiving () + { + if (_messageEventQueue.Count > 0) + _messageEventQueue.Clear (); + + _exitReceiving = new AutoResetEvent (false); + _receivePong = new AutoResetEvent (false); + + Action receive = null; + receive = () => _stream.ReadFrameAsync ( + frame => { + if (acceptFrame (frame) && _readyState != WebSocketState.Closed) { + receive (); + + if (!frame.IsData) + return; + + lock (_forEvent) { + try { + var e = dequeueFromMessageEventQueue (); + if (e != null && _readyState == WebSocketState.Open) + OnMessage.Emit (this, e); + } + catch (Exception ex) { + acceptException (ex, "An exception has occurred while OnMessage."); + } + } + } + else if (_exitReceiving != null) { + _exitReceiving.Set (); + } + }, + ex => acceptException (ex, "An exception has occurred while receiving a message.")); + + receive (); + } + + // As client + private bool validateSecWebSocketAcceptHeader (string value) + { + return value != null && value == CreateResponseKey (_base64Key); + } + + // As client + private bool validateSecWebSocketExtensionsHeader (string value) + { + var compress = _compression != CompressionMethod.None; + if (value == null || value.Length == 0) { + if (compress) + _compression = CompressionMethod.None; + + return true; + } + + if (!compress) + return false; + + var extensions = value.SplitHeaderValue (','); + if (extensions.Contains ( + extension => extension.Trim () != _compression.ToExtensionString ())) + return false; + + _extensions = value; + return true; + } + + // As server + private bool validateSecWebSocketKeyHeader (string value) + { + if (value == null || value.Length == 0) + return false; + + _base64Key = value; + return true; + } + + // As client + private bool validateSecWebSocketProtocolHeader (string value) + { + if (value == null) + return _protocols == null; + + if (_protocols == null || !_protocols.Contains (protocol => protocol == value)) + return false; + + _protocol = value; + return true; + } + + // As server + private bool validateSecWebSocketVersionClientHeader (string value) + { + return value != null && value == _version; + } + + // As client + private bool validateSecWebSocketVersionServerHeader (string value) + { + return value == null || value == _version; + } + + #endregion + + #region Internal Methods + + // As server + internal void Close (HandshakeResponse response) + { + _readyState = WebSocketState.Closing; + + send (response); + closeServerResources (); + + _readyState = WebSocketState.Closed; + } + + // As server + internal void Close (HttpStatusCode code) + { + Close (createHandshakeResponse (code)); + } + + // As server + internal void Close (CloseEventArgs e, byte [] frame, int timeout) + { + lock (_forConn) { + if (_readyState == WebSocketState.Closing || _readyState == WebSocketState.Closed) { + _logger.Info ("Closing the WebSocket connection has already been done."); + return; + } + + _readyState = WebSocketState.Closing; + } + + e.WasClean = closeHandshake (frame, timeout, closeServerResources); + + _readyState = WebSocketState.Closed; + try { + OnClose.Emit (this, e); + } + catch (Exception ex) { + _logger.Fatal ("An exception has occurred while OnClose:\n" + ex.ToString ()); + } + } + + // As server + internal void ConnectAsServer () + { + try { + if (acceptHandshake ()) { + _readyState = WebSocketState.Open; + open (); + } + } + catch (Exception ex) { + acceptException (ex, "An exception has occurred while connecting."); + } + } + + // As client + internal static string CreateBase64Key () + { + var src = new byte [16]; + var rand = new Random (); + rand.NextBytes (src); + + return Convert.ToBase64String (src); + } + + internal static string CreateResponseKey (string base64Key) + { + var buffer = new StringBuilder (base64Key, 64); + buffer.Append (_guid); + SHA1 sha1 = new SHA1CryptoServiceProvider (); + var src = sha1.ComputeHash (Encoding.UTF8.GetBytes (buffer.ToString ())); + + return Convert.ToBase64String (src); + } + + internal bool Ping (byte [] frame, int timeout) + { + try { + AutoResetEvent pong; + return _readyState == WebSocketState.Open && + send (frame) && + (pong = _receivePong) != null && + pong.WaitOne (timeout); + } + catch (Exception ex) { + _logger.Fatal ("An exception has occurred while Ping:\n" + ex.ToString ()); + return false; + } + } + + // As server, used to broadcast + internal void Send (Opcode opcode, byte [] data, Dictionary cache) + { + lock (_forSend) { + lock (_forConn) { + if (_readyState != WebSocketState.Open) + return; + + try { + byte [] cached; + if (!cache.TryGetValue (_compression, out cached)) { + cached = WebSocketFrame.CreateFrame ( + Fin.Final, + opcode, + Mask.Unmask, + data.Compress (_compression), + _compression != CompressionMethod.None) + .ToByteArray (); + + cache.Add (_compression, cached); + } + + _stream.Write (cached); + } + catch (Exception ex) { + _logger.Fatal (ex.ToString ()); + error ("An exception has occurred while sending a data."); + } + } + } + } + + // As server, used to broadcast + internal void Send (Opcode opcode, Stream stream, Dictionary cache) + { + lock (_forSend) { + try { + Stream cached; + if (!cache.TryGetValue (_compression, out cached)) { + cached = stream.Compress (_compression); + cache.Add (_compression, cached); + } + else + cached.Position = 0; + + if (_readyState == WebSocketState.Open) + sendFragmented (opcode, cached, Mask.Unmask, _compression != CompressionMethod.None); + } + catch (Exception ex) { + _logger.Fatal (ex.ToString ()); + error ("An exception has occurred while sending a data."); + } + } + } + + #endregion + + #region Public Methods + + /// + /// Closes the WebSocket connection, and releases all associated resources. + /// + public void Close () + { + var msg = _readyState.CheckIfClosable (); + if (msg != null) { + _logger.Error (msg); + error (msg); + + return; + } + + var send = _readyState == WebSocketState.Open; + close (new PayloadData (), send, send); + } + + /// + /// Closes the WebSocket connection with the specified , and releases all + /// associated resources. + /// + /// + /// This method emits a event if isn't in the + /// allowable range of the WebSocket close status code. + /// + /// + /// A that represents the status code indicating the reason for closure. + /// + public void Close (ushort code) + { + Close (code, null); + } + + /// + /// Closes the WebSocket connection with the specified , and + /// releases all associated resources. + /// + /// + /// One of the enum values, represents the status code indicating + /// the reason for closure. + /// + public void Close (CloseStatusCode code) + { + Close (code, null); + } + + /// + /// Closes the WebSocket connection with the specified and + /// , and releases all associated resources. + /// + /// + /// This method emits a event if isn't in the + /// allowable range of the WebSocket close status code or the size of + /// is greater than 123 bytes. + /// + /// + /// A that represents the status code indicating the reason for closure. + /// + /// + /// A that represents the reason for closure. + /// + public void Close (ushort code, string reason) + { + byte [] data = null; + var msg = _readyState.CheckIfClosable () ?? + code.CheckIfValidCloseStatusCode () ?? + (data = code.Append (reason)).CheckIfValidControlData ("reason"); + + if (msg != null) { + _logger.Error (String.Format ("{0}\ncode: {1} reason: {2}", msg, code, reason)); + error (msg); + + return; + } + + var send = _readyState == WebSocketState.Open && !code.IsReserved (); + close (new PayloadData (data), send, send); + } + + /// + /// Closes the WebSocket connection with the specified and + /// , and releases all associated resources. + /// + /// + /// This method emits a event if the size of is + /// greater than 123 bytes. + /// + /// + /// One of the enum values, represents the status code indicating + /// the reason for closure. + /// + /// + /// A that represents the reason for closure. + /// + public void Close (CloseStatusCode code, string reason) + { + byte [] data = null; + var msg = _readyState.CheckIfClosable () ?? + (data = ((ushort) code).Append (reason)).CheckIfValidControlData ("reason"); + + if (msg != null) { + _logger.Error (String.Format ("{0}\ncode: {1} reason: {2}", msg, code, reason)); + error (msg); + + return; + } + + var send = _readyState == WebSocketState.Open && !code.IsReserved (); + close (new PayloadData (data), send, send); + } + + /// + /// Closes the WebSocket connection asynchronously, and releases all associated resources. + /// + /// + /// This method doesn't wait for the close to be complete. + /// + public void CloseAsync () + { + var msg = _readyState.CheckIfClosable (); + if (msg != null) { + _logger.Error (msg); + error (msg); + + return; + } + + var send = _readyState == WebSocketState.Open; + closeAsync (new PayloadData (), send, send); + } + + /// + /// Closes the WebSocket connection asynchronously with the specified , + /// and releases all associated resources. + /// + /// + /// + /// This method doesn't wait for the close to be complete. + /// + /// + /// This method emits a event if isn't in the + /// allowable range of the WebSocket close status code. + /// + /// + /// + /// A that represents the status code indicating the reason for closure. + /// + public void CloseAsync (ushort code) + { + CloseAsync (code, null); + } + + /// + /// Closes the WebSocket connection asynchronously with the specified + /// , and releases all associated resources. + /// + /// + /// This method doesn't wait for the close to be complete. + /// + /// + /// One of the enum values, represents the status code indicating + /// the reason for closure. + /// + public void CloseAsync (CloseStatusCode code) + { + CloseAsync (code, null); + } + + /// + /// Closes the WebSocket connection asynchronously with the specified and + /// , and releases all associated resources. + /// + /// + /// + /// This method doesn't wait for the close to be complete. + /// + /// + /// This method emits a event if + /// isn't in the allowable range of the WebSocket close status code or the size + /// of is greater than 123 bytes. + /// + /// + /// + /// A that represents the status code indicating the reason for closure. + /// + /// + /// A that represents the reason for closure. + /// + public void CloseAsync (ushort code, string reason) + { + byte [] data = null; + var msg = _readyState.CheckIfClosable () ?? + code.CheckIfValidCloseStatusCode () ?? + (data = code.Append (reason)).CheckIfValidControlData ("reason"); + + if (msg != null) { + _logger.Error (String.Format ("{0}\ncode: {1} reason: {2}", msg, code, reason)); + error (msg); + + return; + } + + var send = _readyState == WebSocketState.Open && !code.IsReserved (); + closeAsync (new PayloadData (data), send, send); + } + + /// + /// Closes the WebSocket connection asynchronously with the specified + /// and , and releases + /// all associated resources. + /// + /// + /// + /// This method doesn't wait for the close to be complete. + /// + /// + /// This method emits a event if the size of + /// is greater than 123 bytes. + /// + /// + /// + /// One of the enum values, represents the status code indicating + /// the reason for closure. + /// + /// + /// A that represents the reason for closure. + /// + public void CloseAsync (CloseStatusCode code, string reason) + { + byte [] data = null; + var msg = _readyState.CheckIfClosable () ?? + (data = ((ushort) code).Append (reason)).CheckIfValidControlData ("reason"); + + if (msg != null) { + _logger.Error (String.Format ("{0}\ncode: {1} reason: {2}", msg, code, reason)); + error (msg); + + return; + } + + var send = _readyState == WebSocketState.Open && !code.IsReserved (); + closeAsync (new PayloadData (data), send, send); + } + + /// + /// Sets header value + /// + public void SetHeader(string header, string value) { + if (_customHeaders == null) { + _customHeaders = new NameValueCollection(); + } + + _customHeaders[header] = value; + } + + /// + /// Establishes a WebSocket connection. + /// + public void Connect () + { + var msg = checkIfCanConnect (); + if (msg != null) { + _logger.Error (msg); + error (msg); + + return; + } + + if (connect ()) + open (); + } + + /// + /// Establishes a WebSocket connection asynchronously. + /// + /// + /// This method doesn't wait for the connect to be complete. + /// + public void ConnectAsync () + { + var msg = checkIfCanConnect (); + if (msg != null) { + _logger.Error (msg); + error (msg); + + return; + } + + Func connector = connect; + connector.BeginInvoke ( + ar => { + if (connector.EndInvoke (ar)) + open (); + }, + null); + } + + /// + /// Sends a Ping using the WebSocket connection. + /// + /// + /// true if the receives a Pong to this Ping in a time; + /// otherwise, false. + /// + public bool Ping () + { + return _client + ? Ping (WebSocketFrame.CreatePingFrame (Mask.Mask).ToByteArray (), 5000) + : Ping (WebSocketFrame.EmptyUnmaskPingData, 1000); + } + + /// + /// Sends a Ping with the specified using the WebSocket connection. + /// + /// + /// true if the receives a Pong to this Ping in a time; + /// otherwise, false. + /// + /// + /// A that represents the message to send. + /// + public bool Ping (string message) + { + if (message == null || message.Length == 0) + return Ping (); + + var data = Encoding.UTF8.GetBytes (message); + var msg = data.CheckIfValidControlData ("message"); + if (msg != null) { + _logger.Error (msg); + error (msg); + + return false; + } + + return _client + ? Ping (WebSocketFrame.CreatePingFrame (Mask.Mask, data).ToByteArray (), 5000) + : Ping (WebSocketFrame.CreatePingFrame (Mask.Unmask, data).ToByteArray (), 1000); + } + + /// + /// Sends a binary using the WebSocket connection. + /// + /// + /// An array of that represents the binary data to send. + /// + public void Send (byte [] data) + { + var msg = _readyState.CheckIfOpen () ?? data.CheckIfValidSendData (); + if (msg != null) { + _logger.Error (msg); + error (msg); + + return; + } + + var len = data.LongLength; + if (len <= FragmentLength) + send ( + Opcode.Binary, + len > 0 && _client && _compression == CompressionMethod.None ? data.Copy (len) : data); + else + send (Opcode.Binary, new MemoryStream (data)); + } + + /// + /// Sends the specified as a binary data + /// using the WebSocket connection. + /// + /// + /// A that represents the file to send. + /// + public void Send (FileInfo file) + { + var msg = _readyState.CheckIfOpen () ?? file.CheckIfValidSendData (); + if (msg != null) { + _logger.Error (msg); + error (msg); + + return; + } + + send (Opcode.Binary, file.OpenRead ()); + } + + /// + /// Sends a text using the WebSocket connection. + /// + /// + /// A that represents the text data to send. + /// + public void Send (string data) + { + var msg = _readyState.CheckIfOpen () ?? data.CheckIfValidSendData (); + if (msg != null) { + _logger.Error (msg); + error (msg); + + return; + } + + var rawData = Encoding.UTF8.GetBytes (data); + if (rawData.LongLength <= FragmentLength) + send (Opcode.Text, rawData); + else + send (Opcode.Text, new MemoryStream (rawData)); + } + + /// + /// Sends a binary asynchronously using the WebSocket connection. + /// + /// + /// This method doesn't wait for the send to be complete. + /// + /// + /// An array of that represents the binary data to send. + /// + /// + /// An Action<bool> delegate that references the method(s) called when the send is + /// complete. A passed to this delegate is true if the send is + /// complete successfully; otherwise, false. + /// + public void SendAsync (byte [] data, Action completed) + { + var msg = _readyState.CheckIfOpen () ?? data.CheckIfValidSendData (); + if (msg != null) { + _logger.Error (msg); + error (msg); + + return; + } + + var len = data.LongLength; + if (len <= FragmentLength) + sendAsync ( + Opcode.Binary, + len > 0 && _client && _compression == CompressionMethod.None ? data.Copy (len) : data, + completed); + else + sendAsync (Opcode.Binary, new MemoryStream (data), completed); + } + + /// + /// Sends the specified as a binary data asynchronously + /// using the WebSocket connection. + /// + /// + /// This method doesn't wait for the send to be complete. + /// + /// + /// A that represents the file to send. + /// + /// + /// An Action<bool> delegate that references the method(s) called when the send is + /// complete. A passed to this delegate is true if the send is + /// complete successfully; otherwise, false. + /// + public void SendAsync (FileInfo file, Action completed) + { + var msg = _readyState.CheckIfOpen () ?? file.CheckIfValidSendData (); + if (msg != null) { + _logger.Error (msg); + error (msg); + + return; + } + + sendAsync (Opcode.Binary, file.OpenRead (), completed); + } + + /// + /// Sends a text asynchronously using the WebSocket connection. + /// + /// + /// This method doesn't wait for the send to be complete. + /// + /// + /// A that represents the text data to send. + /// + /// + /// An Action<bool> delegate that references the method(s) called when the send is + /// complete. A passed to this delegate is true if the send is + /// complete successfully; otherwise, false. + /// + public void SendAsync (string data, Action completed) + { + var msg = _readyState.CheckIfOpen () ?? data.CheckIfValidSendData (); + if (msg != null) { + _logger.Error (msg); + error (msg); + + return; + } + + var rawData = Encoding.UTF8.GetBytes (data); + if (rawData.LongLength <= FragmentLength) + sendAsync (Opcode.Text, rawData, completed); + else + sendAsync (Opcode.Text, new MemoryStream (rawData), completed); + } + + /// + /// Sends a binary data from the specified asynchronously + /// using the WebSocket connection. + /// + /// + /// This method doesn't wait for the send to be complete. + /// + /// + /// A from which contains the binary data to send. + /// + /// + /// An that represents the number of bytes to send. + /// + /// + /// An Action<bool> delegate that references the method(s) called when the send is + /// complete. A passed to this delegate is true if the send is + /// complete successfully; otherwise, false. + /// + public void SendAsync (Stream stream, int length, Action completed) + { + var msg = _readyState.CheckIfOpen () ?? + stream.CheckIfCanRead () ?? + (length < 1 ? "'length' must be greater than 0." : null); + + if (msg != null) { + _logger.Error (msg); + error (msg); + + return; + } + + stream.ReadBytesAsync ( + length, + data => { + var len = data.Length; + if (len == 0) { + msg = "A data cannot be read from 'stream'."; + _logger.Error (msg); + error (msg); + + return; + } + + if (len < length) + _logger.Warn ( + String.Format ( + "A data with 'length' cannot be read from 'stream'.\nexpected: {0} actual: {1}", + length, + len)); + + var sent = len <= FragmentLength + ? send (Opcode.Binary, data) + : send (Opcode.Binary, new MemoryStream (data)); + + if (completed != null) + completed (sent); + }, + ex => { + _logger.Fatal (ex.ToString ()); + error ("An exception has occurred while sending a data."); + }); + } + + /// + /// Sets an HTTP to send with the WebSocket connection request + /// to the server. + /// + /// + /// A that represents the cookie to send. + /// + public void SetCookie (Cookie cookie) + { + lock (_forConn) { + var msg = checkIfAvailable ("SetCookie", false, false) ?? + (cookie == null ? "'cookie' must not be null." : null); + + if (msg != null) { + _logger.Error (msg); + error (msg); + + return; + } + + lock (_cookies.SyncRoot) { + _cookies.SetOrRemove (cookie); + } + } + } + + /// + /// Sets a pair of and for + /// the HTTP authentication (Basic/Digest). + /// + /// + /// A that represents the user name used to authenticate. + /// + /// + /// A that represents the password for + /// used to authenticate. + /// + /// + /// true if the sends the Basic authentication credentials + /// with the first connection request to the server; otherwise, false. + /// + public void SetCredentials (string username, string password, bool preAuth) + { + lock (_forConn) { + var msg = checkIfAvailable ("SetCredentials", false, false); + if (msg == null) { + if (username.IsNullOrEmpty ()) { + _credentials = null; + _preAuth = false; + _logger.Warn ("Credentials was set back to the default."); + + return; + } + + msg = username.Contains (':') || !username.IsText () + ? "'username' contains an invalid character." + : !password.IsNullOrEmpty () && !password.IsText () + ? "'password' contains an invalid character." + : null; + } + + if (msg != null) { + _logger.Error (msg); + error (msg); + + return; + } + + _credentials = new NetworkCredential (username, password, _uri.PathAndQuery); + _preAuth = preAuth; + } + } + + #endregion + + #region Explicit Interface Implementation + + /// + /// Closes the WebSocket connection, and releases all associated resources. + /// + /// + /// This method closes the WebSocket connection with . + /// + void IDisposable.Dispose () + { + Close (CloseStatusCode.Away, null); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/WebSocketException.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/WebSocketException.cs new file mode 100644 index 0000000..1bb64ef --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/WebSocketException.cs @@ -0,0 +1,88 @@ +#region License +/* + * WebSocketException.cs + * + * The MIT License + * + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; + +namespace WebSocketSharp +{ + /// + /// The exception that is thrown when a gets a fatal error. + /// + public class WebSocketException : Exception + { + #region Internal Constructors + + internal WebSocketException () + : this (CloseStatusCode.Abnormal, null, null) + { + } + + internal WebSocketException (string message) + : this (CloseStatusCode.Abnormal, message, null) + { + } + + internal WebSocketException (CloseStatusCode code) + : this (code, null, null) + { + } + + internal WebSocketException (string message, Exception innerException) + : this (CloseStatusCode.Abnormal, message, innerException) + { + } + + internal WebSocketException (CloseStatusCode code, string message) + : this (code, message, null) + { + } + + internal WebSocketException (CloseStatusCode code, string message, Exception innerException) + : base (message ?? code.GetMessage (), innerException) + { + Code = code; + } + + #endregion + + #region Public Properties + + /// + /// Gets the status code indicating the cause for the exception. + /// + /// + /// One of the enum values, represents the status code indicating + /// the cause for the exception. + /// + public CloseStatusCode Code { + get; private set; + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/WebSocketFrame.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/WebSocketFrame.cs new file mode 100644 index 0000000..131b0f5 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/WebSocketFrame.cs @@ -0,0 +1,695 @@ +#region License +/* + * WebSocketFrame.cs + * + * The MIT License + * + * Copyright (c) 2012-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace WebSocketSharp +{ + internal class WebSocketFrame : IEnumerable + { + #region Private Fields + + private byte [] _extPayloadLength; + private Fin _fin; + private Mask _mask; + private byte [] _maskingKey; + private Opcode _opcode; + private PayloadData _payloadData; + private byte _payloadLength; + private Rsv _rsv1; + private Rsv _rsv2; + private Rsv _rsv3; + + #endregion + + #region Internal Static Fields + + internal static readonly byte [] EmptyUnmaskPingData; + + #endregion + + #region Static Constructor + + static WebSocketFrame () + { + EmptyUnmaskPingData = CreatePingFrame (Mask.Unmask).ToByteArray (); + } + + #endregion + + #region Private Constructors + + private WebSocketFrame () + { + } + + #endregion + + #region Public Constructors + + public WebSocketFrame (Opcode opcode, PayloadData payload) + : this (Fin.Final, opcode, Mask.Mask, payload, false) + { + } + + public WebSocketFrame (Opcode opcode, Mask mask, PayloadData payload) + : this (Fin.Final, opcode, mask, payload, false) + { + } + + public WebSocketFrame (Fin fin, Opcode opcode, Mask mask, PayloadData payload) + : this (fin, opcode, mask, payload, false) + { + } + + public WebSocketFrame (Fin fin, Opcode opcode, Mask mask, PayloadData payload, bool compressed) + { + _fin = fin; + _rsv1 = isData (opcode) && compressed ? Rsv.On : Rsv.Off; + _rsv2 = Rsv.Off; + _rsv3 = Rsv.Off; + _opcode = opcode; + _mask = mask; + + var len = payload.Length; + if (len < 126) { + _payloadLength = (byte) len; + _extPayloadLength = new byte [0]; + } + else if (len < 0x010000) { + _payloadLength = (byte) 126; + _extPayloadLength = ((ushort) len).ToByteArrayInternally (ByteOrder.Big); + } + else { + _payloadLength = (byte) 127; + _extPayloadLength = len.ToByteArrayInternally (ByteOrder.Big); + } + + if (mask == Mask.Mask) { + _maskingKey = createMaskingKey (); + payload.Mask (_maskingKey); + } + else { + _maskingKey = new byte [0]; + } + + _payloadData = payload; + } + + #endregion + + #region Public Properties + + public byte [] ExtendedPayloadLength { + get { + return _extPayloadLength; + } + } + + public Fin Fin { + get { + return _fin; + } + } + + public bool IsBinary { + get { + return _opcode == Opcode.Binary; + } + } + + public bool IsClose { + get { + return _opcode == Opcode.Close; + } + } + + public bool IsCompressed { + get { + return _rsv1 == Rsv.On; + } + } + + public bool IsContinuation { + get { + return _opcode == Opcode.Cont; + } + } + + public bool IsControl { + get { + return _opcode == Opcode.Close || _opcode == Opcode.Ping || _opcode == Opcode.Pong; + } + } + + public bool IsData { + get { + return _opcode == Opcode.Binary || _opcode == Opcode.Text; + } + } + + public bool IsFinal { + get { + return _fin == Fin.Final; + } + } + + public bool IsFragmented { + get { + return _fin == Fin.More || _opcode == Opcode.Cont; + } + } + + public bool IsMasked { + get { + return _mask == Mask.Mask; + } + } + + public bool IsPerMessageCompressed { + get { + return (_opcode == Opcode.Binary || _opcode == Opcode.Text) && _rsv1 == Rsv.On; + } + } + + public bool IsPing { + get { + return _opcode == Opcode.Ping; + } + } + + public bool IsPong { + get { + return _opcode == Opcode.Pong; + } + } + + public bool IsText { + get { + return _opcode == Opcode.Text; + } + } + + public ulong Length { + get { + return 2 + (ulong) (_extPayloadLength.Length + _maskingKey.Length) + _payloadData.Length; + } + } + + public Mask Mask { + get { + return _mask; + } + } + + public byte [] MaskingKey { + get { + return _maskingKey; + } + } + + public Opcode Opcode { + get { + return _opcode; + } + } + + public PayloadData PayloadData { + get { + return _payloadData; + } + } + + public byte PayloadLength { + get { + return _payloadLength; + } + } + + public Rsv Rsv1 { + get { + return _rsv1; + } + } + + public Rsv Rsv2 { + get { + return _rsv2; + } + } + + public Rsv Rsv3 { + get { + return _rsv3; + } + } + + #endregion + + #region Private Methods + + private static byte [] createMaskingKey () + { + var key = new byte [4]; + var rand = new Random (); + rand.NextBytes (key); + + return key; + } + + private static string dump (WebSocketFrame frame) + { + var len = frame.Length; + var cnt = (long) (len / 4); + var rem = (int) (len % 4); + + int cntDigit; + string cntFmt; + if (cnt < 10000) { + cntDigit = 4; + cntFmt = "{0,4}"; + } + else if (cnt < 0x010000) { + cntDigit = 4; + cntFmt = "{0,4:X}"; + } + else if (cnt < 0x0100000000) { + cntDigit = 8; + cntFmt = "{0,8:X}"; + } + else { + cntDigit = 16; + cntFmt = "{0,16:X}"; + } + + var spFmt = String.Format ("{{0,{0}}}", cntDigit); + var headerFmt = String.Format ( +@"{0} 01234567 89ABCDEF 01234567 89ABCDEF +{0}+--------+--------+--------+--------+\n", spFmt); + var lineFmt = String.Format ("{0}|{{1,8}} {{2,8}} {{3,8}} {{4,8}}|\n", cntFmt); + var footerFmt = String.Format ("{0}+--------+--------+--------+--------+", spFmt); + + var output = new StringBuilder (64); + Func> linePrinter = () => { + long lineCnt = 0; + return (arg1, arg2, arg3, arg4) => + output.AppendFormat (lineFmt, ++lineCnt, arg1, arg2, arg3, arg4); + }; + + output.AppendFormat (headerFmt, String.Empty); + + var printLine = linePrinter (); + var frameAsBytes = frame.ToByteArray (); + for (long i = 0; i <= cnt; i++) { + var j = i * 4; + if (i < cnt) + printLine ( + Convert.ToString (frameAsBytes [j], 2).PadLeft (8, '0'), + Convert.ToString (frameAsBytes [j + 1], 2).PadLeft (8, '0'), + Convert.ToString (frameAsBytes [j + 2], 2).PadLeft (8, '0'), + Convert.ToString (frameAsBytes [j + 3], 2).PadLeft (8, '0')); + else if (rem > 0) + printLine ( + Convert.ToString (frameAsBytes [j], 2).PadLeft (8, '0'), + rem >= 2 ? Convert.ToString (frameAsBytes [j + 1], 2).PadLeft (8, '0') : String.Empty, + rem == 3 ? Convert.ToString (frameAsBytes [j + 2], 2).PadLeft (8, '0') : String.Empty, + String.Empty); + } + + output.AppendFormat (footerFmt, String.Empty); + return output.ToString (); + } + + private static bool isControl (Opcode opcode) + { + return opcode == Opcode.Close || opcode == Opcode.Ping || opcode == Opcode.Pong; + } + + private static bool isData (Opcode opcode) + { + return opcode == Opcode.Text || opcode == Opcode.Binary; + } + + private static WebSocketFrame parse (byte [] header, Stream stream, bool unmask) + { + /* Header */ + + // FIN + var fin = (header [0] & 0x80) == 0x80 ? Fin.Final : Fin.More; + // RSV1 + var rsv1 = (header [0] & 0x40) == 0x40 ? Rsv.On : Rsv.Off; + // RSV2 + var rsv2 = (header [0] & 0x20) == 0x20 ? Rsv.On : Rsv.Off; + // RSV3 + var rsv3 = (header [0] & 0x10) == 0x10 ? Rsv.On : Rsv.Off; + // Opcode + var opcode = (Opcode) (header [0] & 0x0f); + // MASK + var mask = (header [1] & 0x80) == 0x80 ? Mask.Mask : Mask.Unmask; + // Payload Length + var payloadLen = (byte) (header [1] & 0x7f); + + // Check if correct frame. + var incorrect = isControl (opcode) && fin == Fin.More + ? "A control frame is fragmented." + : !isData (opcode) && rsv1 == Rsv.On + ? "A non data frame is compressed." + : null; + + if (incorrect != null) + throw new WebSocketException (CloseStatusCode.IncorrectData, incorrect); + + // Check if consistent frame. + if (isControl (opcode) && payloadLen > 125) + throw new WebSocketException ( + CloseStatusCode.InconsistentData, + "The length of payload data of a control frame is greater than 125 bytes."); + + var frame = new WebSocketFrame (); + frame._fin = fin; + frame._rsv1 = rsv1; + frame._rsv2 = rsv2; + frame._rsv3 = rsv3; + frame._opcode = opcode; + frame._mask = mask; + frame._payloadLength = payloadLen; + + /* Extended Payload Length */ + + var size = payloadLen < 126 + ? 0 + : payloadLen == 126 + ? 2 + : 8; + + var extPayloadLen = size > 0 ? stream.ReadBytes (size) : new byte [0]; + if (size > 0 && extPayloadLen.Length != size) + throw new WebSocketException ( + "The 'Extended Payload Length' of a frame cannot be read from the data source."); + + frame._extPayloadLength = extPayloadLen; + + /* Masking Key */ + + var masked = mask == Mask.Mask; + var maskingKey = masked ? stream.ReadBytes (4) : new byte [0]; + if (masked && maskingKey.Length != 4) + throw new WebSocketException ( + "The 'Masking Key' of a frame cannot be read from the data source."); + + frame._maskingKey = maskingKey; + + /* Payload Data */ + + ulong len = payloadLen < 126 + ? payloadLen + : payloadLen == 126 + ? extPayloadLen.ToUInt16 (ByteOrder.Big) + : extPayloadLen.ToUInt64 (ByteOrder.Big); + + byte [] data = null; + if (len > 0) { + // Check if allowable payload data length. + if (payloadLen > 126 && len > PayloadData.MaxLength) + throw new WebSocketException ( + CloseStatusCode.TooBig, + "The length of 'Payload Data' of a frame is greater than the allowable length."); + + data = payloadLen > 126 + ? stream.ReadBytes ((long) len, 1024) + : stream.ReadBytes ((int) len); + + if (data.LongLength != (long) len) + throw new WebSocketException ( + "The 'Payload Data' of a frame cannot be read from the data source."); + } + else { + data = new byte [0]; + } + + var payload = new PayloadData (data, masked); + if (masked && unmask) { + payload.Mask (maskingKey); + frame._mask = Mask.Unmask; + frame._maskingKey = new byte [0]; + } + + frame._payloadData = payload; + return frame; + } + + private static string print (WebSocketFrame frame) + { + /* Opcode */ + + var opcode = frame._opcode.ToString (); + + /* Payload Length */ + + var payloadLen = frame._payloadLength; + + /* Extended Payload Length */ + + var ext = frame._extPayloadLength; + var size = ext.Length; + var extPayloadLen = size == 2 + ? ext.ToUInt16 (ByteOrder.Big).ToString () + : size == 8 + ? ext.ToUInt64 (ByteOrder.Big).ToString () + : String.Empty; + + /* Masking Key */ + + var masked = frame.IsMasked; + var maskingKey = masked ? BitConverter.ToString (frame._maskingKey) : String.Empty; + + /* Payload Data */ + + var payload = payloadLen == 0 + ? String.Empty + : size > 0 + ? String.Format ("A {0} frame.", opcode.ToLower ()) + : !masked && !frame.IsFragmented && frame.IsText + ? Encoding.UTF8.GetString (frame._payloadData.ApplicationData) + : frame._payloadData.ToString (); + + var format = +@" FIN: {0} + RSV1: {1} + RSV2: {2} + RSV3: {3} + Opcode: {4} + MASK: {5} + Payload Length: {6} +Extended Payload Length: {7} + Masking Key: {8} + Payload Data: {9}"; + + return String.Format ( + format, + frame._fin, + frame._rsv1, + frame._rsv2, + frame._rsv3, + opcode, + frame._mask, + payloadLen, + extPayloadLen, + maskingKey, + payload); + } + + #endregion + + #region Internal Methods + + internal static WebSocketFrame CreateCloseFrame (Mask mask, PayloadData payload) + { + return new WebSocketFrame (Opcode.Close, mask, payload); + } + + internal static WebSocketFrame CreatePongFrame (Mask mask, PayloadData payload) + { + return new WebSocketFrame (Opcode.Pong, mask, payload); + } + + #endregion + + #region Public Methods + + public static WebSocketFrame CreateCloseFrame (Mask mask, byte [] data) + { + return new WebSocketFrame (Opcode.Close, mask, new PayloadData (data)); + } + + public static WebSocketFrame CreateCloseFrame (Mask mask, CloseStatusCode code, string reason) + { + return new WebSocketFrame ( + Opcode.Close, mask, new PayloadData (((ushort) code).Append (reason))); + } + + public static WebSocketFrame CreateFrame ( + Fin fin, Opcode opcode, Mask mask, byte [] data, bool compressed) + { + return new WebSocketFrame (fin, opcode, mask, new PayloadData (data), compressed); + } + + public static WebSocketFrame CreatePingFrame (Mask mask) + { + return new WebSocketFrame (Opcode.Ping, mask, new PayloadData ()); + } + + public static WebSocketFrame CreatePingFrame (Mask mask, byte [] data) + { + return new WebSocketFrame (Opcode.Ping, mask, new PayloadData (data)); + } + + public IEnumerator GetEnumerator () + { + foreach (var b in ToByteArray ()) + yield return b; + } + + public static WebSocketFrame Parse (byte [] src) + { + return Parse (src, true); + } + + public static WebSocketFrame Parse (Stream stream) + { + return Parse (stream, true); + } + + public static WebSocketFrame Parse (byte [] src, bool unmask) + { + using (var stream = new MemoryStream (src)) + return Parse (stream, unmask); + } + + public static WebSocketFrame Parse (Stream stream, bool unmask) + { + var header = stream.ReadBytes (2); + if (header.Length != 2) + throw new WebSocketException ( + "The header part of a frame cannot be read from the data source."); + + return parse (header, stream, unmask); + } + + public static void ParseAsync (Stream stream, Action completed) + { + ParseAsync (stream, true, completed, null); + } + + public static void ParseAsync ( + Stream stream, Action completed, Action error) + { + ParseAsync (stream, true, completed, error); + } + + public static void ParseAsync ( + Stream stream, bool unmask, Action completed, Action error) + { + stream.ReadBytesAsync ( + 2, + header => { + if (header.Length != 2) + throw new WebSocketException ( + "The header part of a frame cannot be read from the data source."); + + var frame = parse (header, stream, unmask); + if (completed != null) + completed (frame); + }, + error); + } + + public void Print (bool dumped) + { + Console.WriteLine (dumped ? dump (this) : print (this)); + } + + public string PrintToString (bool dumped) + { + return dumped + ? dump (this) + : print (this); + } + + public byte [] ToByteArray () + { + using (var buff = new MemoryStream ()) { + var header = (int) _fin; + header = (header << 1) + (int) _rsv1; + header = (header << 1) + (int) _rsv2; + header = (header << 1) + (int) _rsv3; + header = (header << 4) + (int) _opcode; + header = (header << 1) + (int) _mask; + header = (header << 7) + (int) _payloadLength; + buff.Write (((ushort) header).ToByteArrayInternally (ByteOrder.Big), 0, 2); + + if (_payloadLength > 125) + buff.Write (_extPayloadLength, 0, _extPayloadLength.Length); + + if (_mask == Mask.Mask) + buff.Write (_maskingKey, 0, _maskingKey.Length); + + if (_payloadLength > 0) { + var payload = _payloadData.ToByteArray (); + if (_payloadLength < 127) + buff.Write (payload, 0, payload.Length); + else + buff.WriteBytes (payload); + } + + buff.Close (); + return buff.ToArray (); + } + } + + public override string ToString () + { + return BitConverter.ToString (ToByteArray ()); + } + + #endregion + + #region Explicitly Implemented Interface Members + + IEnumerator IEnumerable.GetEnumerator () + { + return GetEnumerator (); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/WebSocketState.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/WebSocketState.cs new file mode 100644 index 0000000..67c2930 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/WebSocketState.cs @@ -0,0 +1,65 @@ +#region License +/* + * WebSocketState.cs + * + * The MIT License + * + * Copyright (c) 2010-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; + +namespace WebSocketSharp +{ + /// + /// Contains the values of the state of the WebSocket connection. + /// + /// + /// The values of the state are defined in + /// The WebSocket + /// API. + /// + public enum WebSocketState : ushort + { + /// + /// Equivalent to numeric value 0. + /// Indicates that the connection has not yet been established. + /// + Connecting = 0, + /// + /// Equivalent to numeric value 1. + /// Indicates that the connection is established and the communication is possible. + /// + Open = 1, + /// + /// Equivalent to numeric value 2. + /// Indicates that the connection is going through the closing handshake or + /// the WebSocket.Close method has been invoked. + /// + Closing = 2, + /// + /// Equivalent to numeric value 3. + /// Indicates that the connection has been closed or couldn't be opened. + /// + Closed = 3 + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/WebSocketStream.cs b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/WebSocketStream.cs new file mode 100644 index 0000000..4406b43 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/WebSocketStream.cs @@ -0,0 +1,287 @@ +#region License +/* + * WebSocketStream.cs + * + * The MIT License + * + * Copyright (c) 2010-2014 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Sockets; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading; +using WebSocketSharp.Net; +using WebSocketSharp.Net.Security; + +namespace WebSocketSharp +{ + internal class WebSocketStream : IDisposable + { + #region Private Const Fields + + private const int _handshakeHeadersLimitLen = 8192; + + #endregion + + #region Private Fields + + private object _forWrite; + private Stream _innerStream; + private bool _secure; + + #endregion + + #region Internal Constructors + + internal WebSocketStream (Stream innerStream, bool secure) + { + _innerStream = innerStream; + _secure = secure; + _forWrite = new object (); + } + + #endregion + + #region Public Constructors + + public WebSocketStream (NetworkStream innerStream) + : this (innerStream, false) + { + } + + public WebSocketStream (SslStream innerStream) + : this (innerStream, true) + { + } + + #endregion + + #region Public Properties + + public bool DataAvailable { + get { + return _secure + ? ((SslStream) _innerStream).DataAvailable + : ((NetworkStream) _innerStream).DataAvailable; + } + } + + public bool IsSecure { + get { + return _secure; + } + } + + #endregion + + #region Private Methods + + private static byte [] readHandshakeEntityBody (Stream stream, string length) + { + long len; + if (!Int64.TryParse (length, out len)) + throw new ArgumentException ("Cannot be parsed.", "length"); + + if (len < 0) + throw new ArgumentOutOfRangeException ("length", "Less than zero."); + + return len > 1024 + ? stream.ReadBytes (len, 1024) + : len > 0 + ? stream.ReadBytes ((int) len) + : null; + } + + private static string [] readHandshakeHeaders (Stream stream) + { + var buff = new List (); + var count = 0; + Action add = i => { + buff.Add ((byte) i); + count++; + }; + + var read = false; + while (count < _handshakeHeadersLimitLen) { + if (stream.ReadByte ().EqualsWith ('\r', add) && + stream.ReadByte ().EqualsWith ('\n', add) && + stream.ReadByte ().EqualsWith ('\r', add) && + stream.ReadByte ().EqualsWith ('\n', add)) { + read = true; + break; + } + } + + if (!read) + throw new WebSocketException ( + "The header part of a handshake is greater than the limit length."); + + var crlf = "\r\n"; + return Encoding.UTF8.GetString (buff.ToArray ()) + .Replace (crlf + " ", " ") + .Replace (crlf + "\t", " ") + .Split (new [] { crlf }, StringSplitOptions.RemoveEmptyEntries); + } + + #endregion + + #region Internal Methods + + internal T ReadHandshake (Func parser, int millisecondsTimeout) + where T : HandshakeBase + { + var timeout = false; + var timer = new Timer ( + state => { + timeout = true; + _innerStream.Close (); + }, + null, + millisecondsTimeout, + -1); + + T handshake = null; + Exception exception = null; + try { + handshake = parser (readHandshakeHeaders (_innerStream)); + var contentLen = handshake.Headers ["Content-Length"]; + if (contentLen != null && contentLen.Length > 0) + handshake.EntityBodyData = readHandshakeEntityBody (_innerStream, contentLen); + } + catch (Exception ex) { + exception = ex; + } + finally { + timer.Change (-1, -1); + timer.Dispose (); + } + + var msg = timeout + ? "A timeout has occurred while receiving a handshake." + : exception != null + ? "An exception has occurred while receiving a handshake." + : null; + + if (msg != null) + throw new WebSocketException (msg, exception); + + return handshake; + } + + internal bool Write (byte [] data) + { + lock (_forWrite) { + try { + _innerStream.Write (data, 0, data.Length); + return true; + } + catch { + return false; + } + } + } + + #endregion + + #region Public Methods + + public void Close () + { + _innerStream.Close (); + } + + public static WebSocketStream CreateClientStream ( + TcpClient client, + bool secure, + string host, + System.Net.Security.RemoteCertificateValidationCallback validationCallback) + { + var netStream = client.GetStream (); + if (secure) { + if (validationCallback == null) + validationCallback = (sender, certificate, chain, sslPolicyErrors) => true; + + var sslStream = new SslStream (netStream, false, validationCallback); + sslStream.AuthenticateAsClient (host); + + return new WebSocketStream (sslStream); + } + + return new WebSocketStream (netStream); + } + + public static WebSocketStream CreateServerStream ( + TcpClient client, bool secure, X509Certificate cert) + { + var netStream = client.GetStream (); + if (secure) { + var sslStream = new SslStream (netStream, false); + sslStream.AuthenticateAsServer (cert); + + return new WebSocketStream (sslStream); + } + + return new WebSocketStream (netStream); + } + + public void Dispose () + { + _innerStream.Dispose (); + } + + public WebSocketFrame ReadFrame () + { + return WebSocketFrame.Parse (_innerStream, true); + } + + public void ReadFrameAsync (Action completed, Action error) + { + WebSocketFrame.ParseAsync (_innerStream, true, completed, error); + } + + public HandshakeRequest ReadHandshakeRequest () + { + return ReadHandshake (HandshakeRequest.Parse, 90000); + } + + public HandshakeResponse ReadHandshakeResponse () + { + return ReadHandshake (HandshakeResponse.Parse, 90000); + } + + public bool WriteFrame (WebSocketFrame frame) + { + return Write (frame.ToByteArray ()); + } + + public bool WriteHandshake (HandshakeBase handshake) + { + return Write (handshake.ToByteArray ()); + } + + #endregion + } +} diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/websocket-sharp.snk b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/websocket-sharp.snk new file mode 100644 index 0000000..a2546f3 Binary files /dev/null and b/Examples/MeshReceivingUnityClient/Assets/SocketIO/WebsocketSharp/websocket-sharp.snk differ diff --git a/Examples/MeshReceivingUnityClient/Assets/SocketIO/readme.txt b/Examples/MeshReceivingUnityClient/Assets/SocketIO/readme.txt new file mode 100644 index 0000000..8950b4a --- /dev/null +++ b/Examples/MeshReceivingUnityClient/Assets/SocketIO/readme.txt @@ -0,0 +1,147 @@ +-------------------------------------------------------------------------------- + SocketIO for Unity - v1.0.0 +-------------------------------------------------------------------------------- + +# Overview # + + This plugin allows you to integrate your Unity game with Socket.IO back-end + It implements the protocol described at socket.io-protocol github repo. + ( https://github.com/automattic/socket.io-protocol ) + + While connected, Socket.IO run on it's own thread to avoid blocking the main + thread. Events are queued and dispatched on the next frame they are received. + + +# Support # + + fpanettieri@gmail.com + skype: fabio.panettieri + + +# Quick Start # + + In order to start using Socket.IO in your project you need to: + 1. Drag the SocketIO prefab from SocketIO/Prefab/ to your scene. + 2. Configure the url where your Socket.IO server is listening. + 3. Toggle the autoconnect flag if you want it to be always running. + 4. That's it! You can now start using Socket.IO in your game. + + +# How to use # + + 1. Obtaining the Socket.IO component reference + + GameObject go = GameObject.Find("SocketIO"); + socket = go.GetComponent(); + + Bear in mind that using GameObject.Find might be pretty expensive, you + might want to store that reference in a variable for later use. + + + 2. Receiving events + + Using the socket reference you can receive custom events + + public void Start(){ + socket.On("boop", TestBoop); + } + + public void TestBoop(SocketIOEvent e){ + Debug.Log(string.Format("[name: {0}, data: {1}]", e.name, e.data)); + } + + Also, you can also use lambda expresions as callbacks + + socket.On("boop", (SocketIOEvent e) => { + Debug.Log(string.Format("[name: {0}, data: {1}]", e.name, e.data)); + }); + + + 3. Sending events + + Besides listening to Socket.IO events or your custom events, you can + use send information to Socket.IO server using the Emit method. + + a) Sending plain messages + socket.Emit("user:login"); + + b) Sending additional data + + Dictionary data = new Dictionary(); + data["email"] = "some@email.com"; + data["pass"] = Encrypt("1234"); + socket.Emit("user:login", new JSONObject(data)); + + c) Sometimes, you might want to get a callback when the client confirmed + the message reception. To do this, simply pass a function as the last + parameter of .Emit() + + socket.Emit("user:login", OnLogin); + socket.Emit("user:login", new JSONObject(data), OnLogin); + + + 4. Obtaining current socket id (socket.sid) + + public void Start(){ + socket.On("open", OnSocketOpen); + } + + public void OnSocketOpen(SocketIOEvent ev){ + Debug.Log("updated socket id " + socket.sid); + } + + + 5. Namespace Support + Not implemented yet! + + + 6. Binary Events + Not implemented yet! + + +# Examples # + + This package also includes a minimalist test that you might want to use + to verify that you have setup your environment properly. + + 1. Navigate to the server directory + cd PATH/TO/PROJECT/Assets/SocketIO/Server + + 2. Install Socket.IO server package + npm install socket.io + + 3. (Optional) Enable debug mode + Windows: set DEBUG=* + Mac: export DEBUG=* + + 4. Run test server + node ./beep.js + + 5. Open the test scene + SocketIO/Scenes/SocketIOTest + + 6. Run the scene. Some debug message will be printed to Unity console. + + 7. Open SocketIO/Scripts/Test/TestSocketIO.cs to check what's going on. + + +# Troubleshooting # + + This is the first release of the plugin, so error may appear. + In order to track them, I've included some debug code that it's + only compiled when you uncomment some flags. + + In order to enable debug messages you might: + 1. Navigate to SocketIO/Scripts/SocketIO + 2. Open files Decoder.cs, Encoder.cs and SocketIOComponent.cs + 3. Uncomment the following line + #define SOCKET_IO_DEBUG + 4. Run your game again. This time messages sent and received will be + logged to Unity console. Hopefully you might find some lead to the + source of your problems / bugs. + 5. Remember to comment it again after you used it. + + +# License # + + SocketIO for Unity is provided under The MIT License diff --git a/Examples/MeshReceivingUnityClient/ProjectSettings/AudioManager.asset b/Examples/MeshReceivingUnityClient/ProjectSettings/AudioManager.asset new file mode 100644 index 0000000..2fa6ae1 Binary files /dev/null and b/Examples/MeshReceivingUnityClient/ProjectSettings/AudioManager.asset differ diff --git a/Examples/MeshReceivingUnityClient/ProjectSettings/ClusterInputManager.asset b/Examples/MeshReceivingUnityClient/ProjectSettings/ClusterInputManager.asset new file mode 100644 index 0000000..681ba53 Binary files /dev/null and b/Examples/MeshReceivingUnityClient/ProjectSettings/ClusterInputManager.asset differ diff --git a/Examples/MeshReceivingUnityClient/ProjectSettings/DynamicsManager.asset b/Examples/MeshReceivingUnityClient/ProjectSettings/DynamicsManager.asset new file mode 100644 index 0000000..8572160 Binary files /dev/null and b/Examples/MeshReceivingUnityClient/ProjectSettings/DynamicsManager.asset differ diff --git a/Examples/MeshReceivingUnityClient/ProjectSettings/EditorBuildSettings.asset b/Examples/MeshReceivingUnityClient/ProjectSettings/EditorBuildSettings.asset new file mode 100644 index 0000000..3dc1810 Binary files /dev/null and b/Examples/MeshReceivingUnityClient/ProjectSettings/EditorBuildSettings.asset differ diff --git a/Examples/MeshReceivingUnityClient/ProjectSettings/EditorSettings.asset b/Examples/MeshReceivingUnityClient/ProjectSettings/EditorSettings.asset new file mode 100644 index 0000000..0e5dac7 Binary files /dev/null and b/Examples/MeshReceivingUnityClient/ProjectSettings/EditorSettings.asset differ diff --git a/Examples/MeshReceivingUnityClient/ProjectSettings/GraphicsSettings.asset b/Examples/MeshReceivingUnityClient/ProjectSettings/GraphicsSettings.asset new file mode 100644 index 0000000..a7c2e12 Binary files /dev/null and b/Examples/MeshReceivingUnityClient/ProjectSettings/GraphicsSettings.asset differ diff --git a/Examples/MeshReceivingUnityClient/ProjectSettings/InputManager.asset b/Examples/MeshReceivingUnityClient/ProjectSettings/InputManager.asset new file mode 100644 index 0000000..fcc90d5 Binary files /dev/null and b/Examples/MeshReceivingUnityClient/ProjectSettings/InputManager.asset differ diff --git a/Examples/MeshReceivingUnityClient/ProjectSettings/NavMeshAreas.asset b/Examples/MeshReceivingUnityClient/ProjectSettings/NavMeshAreas.asset new file mode 100644 index 0000000..6701415 Binary files /dev/null and b/Examples/MeshReceivingUnityClient/ProjectSettings/NavMeshAreas.asset differ diff --git a/Examples/MeshReceivingUnityClient/ProjectSettings/NetworkManager.asset b/Examples/MeshReceivingUnityClient/ProjectSettings/NetworkManager.asset new file mode 100644 index 0000000..7f2846d Binary files /dev/null and b/Examples/MeshReceivingUnityClient/ProjectSettings/NetworkManager.asset differ diff --git a/Examples/MeshReceivingUnityClient/ProjectSettings/Physics2DSettings.asset b/Examples/MeshReceivingUnityClient/ProjectSettings/Physics2DSettings.asset new file mode 100644 index 0000000..fe7b554 Binary files /dev/null and b/Examples/MeshReceivingUnityClient/ProjectSettings/Physics2DSettings.asset differ diff --git a/Examples/MeshReceivingUnityClient/ProjectSettings/ProjectSettings.asset b/Examples/MeshReceivingUnityClient/ProjectSettings/ProjectSettings.asset new file mode 100644 index 0000000..e155868 Binary files /dev/null and b/Examples/MeshReceivingUnityClient/ProjectSettings/ProjectSettings.asset differ diff --git a/Examples/MeshReceivingUnityClient/ProjectSettings/ProjectVersion.txt b/Examples/MeshReceivingUnityClient/ProjectSettings/ProjectVersion.txt new file mode 100644 index 0000000..ca09a3d --- /dev/null +++ b/Examples/MeshReceivingUnityClient/ProjectSettings/ProjectVersion.txt @@ -0,0 +1 @@ +m_EditorVersion: 5.6.0f3 diff --git a/Examples/MeshReceivingUnityClient/ProjectSettings/QualitySettings.asset b/Examples/MeshReceivingUnityClient/ProjectSettings/QualitySettings.asset new file mode 100644 index 0000000..4fc8c3f Binary files /dev/null and b/Examples/MeshReceivingUnityClient/ProjectSettings/QualitySettings.asset differ diff --git a/Examples/MeshReceivingUnityClient/ProjectSettings/TagManager.asset b/Examples/MeshReceivingUnityClient/ProjectSettings/TagManager.asset new file mode 100644 index 0000000..a20a78f Binary files /dev/null and b/Examples/MeshReceivingUnityClient/ProjectSettings/TagManager.asset differ diff --git a/Examples/MeshReceivingUnityClient/ProjectSettings/TimeManager.asset b/Examples/MeshReceivingUnityClient/ProjectSettings/TimeManager.asset new file mode 100644 index 0000000..2f91712 Binary files /dev/null and b/Examples/MeshReceivingUnityClient/ProjectSettings/TimeManager.asset differ diff --git a/Examples/MeshReceivingUnityClient/ProjectSettings/UnityAdsSettings.asset b/Examples/MeshReceivingUnityClient/ProjectSettings/UnityAdsSettings.asset new file mode 100644 index 0000000..279183b Binary files /dev/null and b/Examples/MeshReceivingUnityClient/ProjectSettings/UnityAdsSettings.asset differ diff --git a/Examples/MeshReceivingUnityClient/ProjectSettings/UnityConnectSettings.asset b/Examples/MeshReceivingUnityClient/ProjectSettings/UnityConnectSettings.asset new file mode 100644 index 0000000..148cc17 Binary files /dev/null and b/Examples/MeshReceivingUnityClient/ProjectSettings/UnityConnectSettings.asset differ diff --git a/Examples/MeshReceivingUnityClient/README.md b/Examples/MeshReceivingUnityClient/README.md new file mode 100644 index 0000000..58fa338 --- /dev/null +++ b/Examples/MeshReceivingUnityClient/README.md @@ -0,0 +1,8 @@ +# MeshReceivingUnityClient +Unity file to receive streamed mesh binary data. + +## Dependencies + +- [Socket.IO for Unity](https://www.assetstore.unity3d.com/en/#!/content/21721) +- [ZeroFormatter.Unity](https://github.com/neuecc/ZeroFormatter) +- [Json.NET](http://www.newtonsoft.com/json) diff --git a/Examples/MeshStreamingServer/app.js b/Examples/MeshStreamingServer/app.js new file mode 100644 index 0000000..32fae11 --- /dev/null +++ b/Examples/MeshStreamingServer/app.js @@ -0,0 +1,60 @@ +var receivedData + +// 1.Initialize Module +var fs = require("fs"); +var server = require("http").createServer(function(req, res) { + res.writeHead(200, {"Content-Type":"text/html"}); + var output = fs.readFileSync("./index.html", "utf-8"); + res.end(output); +}).listen(8080); +var io = require("socket.io").listen(server); + +console.log("server started."); + +// User Hash +var userHash = {}; + +// 2.Event Definition +io.sockets.on("connection", function (socket) { + + // On Connected Custom Event (Save connected user and notify others) + socket.on("connected", function (name) { + var msg = name + " is connected."; + userHash[socket.id] = name; + io.sockets.emit("publish", {value: msg}); + console.log(msg); + }); + + // Custom Event to Send Message + socket.on("publish", function (data) { + console.log("published"); + io.sockets.emit("publish", {value:data.value}); + }); + + // On Disconnected Event(Delete connected user and notify others) + socket.on("disconnect", function () { + if (userHash[socket.id]) { + var msg = userHash[socket.id] + " is disconnected."; + delete userHash[socket.id]; + io.sockets.emit("publish", {value: msg}); + console.log(msg); + + //io.sockets.emit("unity",receivedData); + } + }); + + socket.on("gh", function (data) { + console.log(data.length); + var sendingData = []; + for (var i = 0; i < data.length; i++){ + var meshData = data[i]; + sendingData.push(meshData.mesh.toString("base64")); + } + io.sockets.emit("unity",{"meshes":sendingData}); + //console.log("data received: " + data.mesh.length);//data.mesh); + //receivedData = data; + //io.sockets.emit("unity",{"mesh":data.mesh.toString("base64")}); + + }); + +}); diff --git a/Examples/MeshStreamingServer/index.html b/Examples/MeshStreamingServer/index.html new file mode 100644 index 0000000..8c91cde --- /dev/null +++ b/Examples/MeshStreamingServer/index.html @@ -0,0 +1,44 @@ + + + + Debug Chat + + + + +
+ + + + diff --git a/Examples/MeshStreamingServer/package.json b/Examples/MeshStreamingServer/package.json new file mode 100644 index 0000000..dc74779 --- /dev/null +++ b/Examples/MeshStreamingServer/package.json @@ -0,0 +1,26 @@ +{ + "name": "meshstreamingserver", + "version": "0.0.1", + "description": "A server to receive and send grasshopper mesh binary data.", + "main": "app.js", + "dependencies": { + "socket.io": "^1.7.3" + }, + "devDependencies": {}, + "scripts": { + "test": "test" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/jhorikawa/MeshStreamingServer.git" + }, + "keywords": [ + "Node.JS" + ], + "author": "Junichiro Horikawa", + "license": "ISC", + "bugs": { + "url": "https://github.com/jhorikawa/MeshStreamingServer/issues" + }, + "homepage": "https://github.com/jhorikawa/MeshStreamingServer#readme" +} diff --git a/MeshStreaming.sln b/MeshStreamingGH/MeshStreaming.sln similarity index 100% rename from MeshStreaming.sln rename to MeshStreamingGH/MeshStreaming.sln diff --git a/MeshStreaming/ConnectSocketComponent.cs b/MeshStreamingGH/MeshStreaming/ConnectSocketComponent.cs similarity index 100% rename from MeshStreaming/ConnectSocketComponent.cs rename to MeshStreamingGH/MeshStreaming/ConnectSocketComponent.cs diff --git a/MeshStreamingGH/MeshStreaming/CustomMesh.cs b/MeshStreamingGH/MeshStreaming/CustomMesh.cs new file mode 100644 index 0000000..521ba62 --- /dev/null +++ b/MeshStreamingGH/MeshStreaming/CustomMesh.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using ZeroFormatter; + +namespace MeshStreaming +{ + [ZeroFormattable] + public class CustomMesh + { + [Index(0)] + public virtual IList vertices { get; set; } + + [Index(1)] + public virtual IList uvs { get; set; } + + [Index(2)] + public virtual IList normals { get; set; } + + [Index(3)] + public virtual IList faces { get; set; } + + public CustomMesh() { } + } +} diff --git a/MeshStreaming/MeshDeserializeComponent.cs b/MeshStreamingGH/MeshStreaming/MeshDeserializeComponent.cs similarity index 100% rename from MeshStreaming/MeshDeserializeComponent.cs rename to MeshStreamingGH/MeshStreaming/MeshDeserializeComponent.cs diff --git a/MeshStreaming/MeshSerializeComponent.cs b/MeshStreamingGH/MeshStreaming/MeshSerializeComponent.cs similarity index 100% rename from MeshStreaming/MeshSerializeComponent.cs rename to MeshStreamingGH/MeshStreaming/MeshSerializeComponent.cs diff --git a/MeshStreaming/MeshStreaming.csproj b/MeshStreamingGH/MeshStreaming/MeshStreaming.csproj similarity index 100% rename from MeshStreaming/MeshStreaming.csproj rename to MeshStreamingGH/MeshStreaming/MeshStreaming.csproj diff --git a/MeshStreaming/MeshStreamingInfo.cs b/MeshStreamingGH/MeshStreaming/MeshStreamingInfo.cs similarity index 100% rename from MeshStreaming/MeshStreamingInfo.cs rename to MeshStreamingGH/MeshStreaming/MeshStreamingInfo.cs diff --git a/MeshStreaming/Properties/AssemblyInfo.cs b/MeshStreamingGH/MeshStreaming/Properties/AssemblyInfo.cs similarity index 100% rename from MeshStreaming/Properties/AssemblyInfo.cs rename to MeshStreamingGH/MeshStreaming/Properties/AssemblyInfo.cs diff --git a/MeshStreaming/Properties/Resources.Designer.cs b/MeshStreamingGH/MeshStreaming/Properties/Resources.Designer.cs similarity index 100% rename from MeshStreaming/Properties/Resources.Designer.cs rename to MeshStreamingGH/MeshStreaming/Properties/Resources.Designer.cs diff --git a/MeshStreaming/Properties/Resources.resx b/MeshStreamingGH/MeshStreaming/Properties/Resources.resx similarity index 100% rename from MeshStreaming/Properties/Resources.resx rename to MeshStreamingGH/MeshStreaming/Properties/Resources.resx diff --git a/MeshStreaming/ReceiveMeshComponent.cs b/MeshStreamingGH/MeshStreaming/ReceiveMeshComponent.cs similarity index 100% rename from MeshStreaming/ReceiveMeshComponent.cs rename to MeshStreamingGH/MeshStreaming/ReceiveMeshComponent.cs diff --git a/MeshStreaming/SaveBytesComponent.cs b/MeshStreamingGH/MeshStreaming/SaveBytesComponent.cs similarity index 100% rename from MeshStreaming/SaveBytesComponent.cs rename to MeshStreamingGH/MeshStreaming/SaveBytesComponent.cs diff --git a/MeshStreaming/SendMeshComponent.cs b/MeshStreamingGH/MeshStreaming/SendMeshComponent.cs similarity index 76% rename from MeshStreaming/SendMeshComponent.cs rename to MeshStreamingGH/MeshStreaming/SendMeshComponent.cs index 4985884..b427925 100644 --- a/MeshStreaming/SendMeshComponent.cs +++ b/MeshStreamingGH/MeshStreaming/SendMeshComponent.cs @@ -25,7 +25,7 @@ public SendMeshComponent() /// protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager) { - pManager.AddGenericParameter("Bytes", "Bytes", "Bytes to send", GH_ParamAccess.item); + pManager.AddGenericParameter("Bytes", "Bytes", "Bytes to send", GH_ParamAccess.list);//item); pManager.AddGenericParameter("Socket", "Socket", "Socket Data", GH_ParamAccess.item); pManager.AddBooleanParameter("Send", "Send", "Send data", GH_ParamAccess.item); } @@ -36,7 +36,7 @@ protected override void RegisterInputParams(GH_Component.GH_InputParamManager pM protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager) { pManager.AddTextParameter("Status", "Status", "Socket status", GH_ParamAccess.item); - pManager.AddIntegerParameter("Length", "Length", "Sent data length", GH_ParamAccess.item); + pManager.AddIntegerParameter("Length", "Length", "Sent data length", GH_ParamAccess.list); } /// @@ -45,11 +45,14 @@ protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager /// The DA object is used to retrieve from inputs and store in outputs. protected override void SolveInstance(IGH_DataAccess DA) { - byte[] bytes = new byte[0]; + //byte[] bytes = new byte[0]; + List bytesList = new List(); Socket socket = null; bool send = false; + List dataLengthList = new List(); - if (!DA.GetData(0, ref bytes)) return; + //if (!DA.GetData(0, ref bytes)) return; + if (!DA.GetDataList(0, bytesList)) return; if (!DA.GetData(1, ref socket)) return; if (!DA.GetData(2, ref send)) return; @@ -58,14 +61,22 @@ protected override void SolveInstance(IGH_DataAccess DA) { if (send) { - var obj = new JObject(); - obj["mesh"] = bytes; - - - socket.Emit("gh", obj); + var objs = new JArray(); + for (int i = 0; i < bytesList.Count; i++) + { + var obj = new JObject(); + obj["mesh"] = bytesList[i]; + objs.Add(obj); + dataLengthList.Add(bytesList[i].Length); + } + + //var obj = new JObject(); + //obj["mesh"] = bytes; + + socket.Emit("gh", objs); DA.SetData(0, "Data Sent"); - DA.SetData(1, bytes.Length); + DA.SetDataList(1, dataLengthList); }else { DA.SetData(0, "Data Not Sent"); diff --git a/MeshStreaming/Utils.cs b/MeshStreamingGH/MeshStreaming/Utils.cs similarity index 100% rename from MeshStreaming/Utils.cs rename to MeshStreamingGH/MeshStreaming/Utils.cs diff --git a/MeshStreaming/packages.config b/MeshStreamingGH/MeshStreaming/packages.config similarity index 100% rename from MeshStreaming/packages.config rename to MeshStreamingGH/MeshStreaming/packages.config