diff --git a/websocket-sharp/Net/CookieCollection.cs b/websocket-sharp/Net/CookieCollection.cs index 0cfa7203..e9f7c054 100644 --- a/websocket-sharp/Net/CookieCollection.cs +++ b/websocket-sharp/Net/CookieCollection.cs @@ -1,35 +1,43 @@ -// -// CookieCollection.cs -// Copied from System.Net.CookieCollection.cs -// -// Authors: -// Lawrence Pit (loz@cable.a2000.nl) -// Gonzalo Paniagua Javier (gonzalo@ximian.com) -// Sebastien Pouliot (sebastien@ximian.com) -// sta (sta.blockhead@gmail.com) -// -// Copyright (c) 2004,2009 Novell, Inc (http://www.novell.com) -// Copyright (c) 2012-2013 sta.blockhead (sta.blockhead@gmail.com) -// -// 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. -// +#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; @@ -39,553 +47,552 @@ using System.Linq; using System.Runtime.Serialization; using System.Text; -namespace WebSocketSharp.Net { - - /// - /// Provides a collection container for instances of the class. - /// - [Serializable] - public class CookieCollection : ICollection, IEnumerable - { - // not 100% identical to MS implementation - sealed class CookieCollectionComparer : IComparer - { - public int Compare (Cookie x, Cookie y) - { - if (x == null || y == null) - return 0; - - int c1 = x.Name.Length + x.Value.Length; - int c2 = y.Name.Length + y.Value.Length; - - return (c1 - c2); - } - } - - #region Private Static Fields - - static CookieCollectionComparer Comparer = new CookieCollectionComparer (); - - #endregion - - #region Private Fields - - List list; - 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 { - return from cookie in list - orderby cookie.Version, - cookie.Name, - cookie.Path.Length descending - select cookie; - } - } - - #endregion - - #region Public Properties - - /// - /// Gets the number of cookies contained in the . - /// - /// - /// An that indicates the number of cookies contained in the . - /// - public int Count { - get { return list.Count; } - } - - // LAMESPEC: So how is one supposed to create a writable CookieCollection - // instance?? We simply ignore this property, as this collection is always - // writable. - // - /// - /// Gets a value indicating whether the is read-only. - /// - /// - /// true if the is read-only; otherwise, false. - /// The default is true. - /// - public bool IsReadOnly { - get { return true; } - } - - /// - /// Gets a value indicating whether access to the is thread safe. - /// - /// - /// true if access to the is thread safe; otherwise, false. - /// The default is false. - /// - public bool IsSynchronized { - get { return false; } - } - - /// - /// Gets the with the specified from the . - /// - /// - /// A with the specified in the . - /// - /// - /// An is the zero-based index of the to find. - /// - /// - /// is less than zero or is greater than or - /// equal to . - /// - 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 . - /// - /// - /// A with the specified in the . - /// - /// - /// A is 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 to use to synchronize access to the . - /// - /// - /// An to use to synchronize access to the . - /// - public Object SyncRoot { - get { - if (sync == null) - sync = new object (); - - return sync; - } - } - - #endregion - - #region Private Methods - - static CookieCollection ParseRequest (string value) - { - var cookies = new CookieCollection (); - - Cookie cookie = null; - int version = 0; - string [] pairs = Split(value).ToArray(); - for (int i = 0; i < pairs.Length; i++) { - string 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; - int 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; - } - - static CookieCollection ParseResponse (string value) - { - var cookies = new CookieCollection (); - - Cookie cookie = null; - string [] pairs = Split(value).ToArray(); - for (int i = 0; i < pairs.Length; i++) { - string 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 string [] { "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)) { - int 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; - int 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; - } - - int SearchCookie (Cookie cookie) - { - string name = cookie.Name; - string path = cookie.Path; - string domain = cookie.Domain; - int version = cookie.Version; - - for (int i = list.Count - 1; i >= 0; i--) { - Cookie c = list [i]; - if (!c.Name.Equals (name, StringComparison.InvariantCultureIgnoreCase)) - continue; - - if (!c.Path.Equals (path, StringComparison.InvariantCulture)) - continue; - - if (!c.Domain.Equals (domain, StringComparison.InvariantCultureIgnoreCase)) - continue; - - if (c.Version != version) - continue; - - return i; - } - - return -1; - } - - static IEnumerable Split (string value) - { - return value.SplitHeaderValue (',', ';'); - } - - #endregion - - #region Internal Methods - - internal static CookieCollection Parse (string value, bool response) - { - return response - ? ParseResponse (value) - : ParseRequest (value); - } - - internal void SetOrRemove (Cookie cookie) - { - int pos = SearchCookie (cookie); - if (pos == -1) { - if (!cookie.Expired) - list.Add (cookie); - } - else { - if (!cookie.Expired) - list [pos] = cookie; - else - list.RemoveAt (pos); - } - } - - internal void SetOrRemove (CookieCollection cookies) - { - foreach (Cookie cookie in cookies) - SetOrRemove (cookie); - } - - internal void Sort () - { - if (list.Count > 0) - list.Sort (Comparer); - } - - #endregion - - #region Public Methods - - /// - /// Add the specified to the . - /// - /// - /// A to add to the . - /// - /// - /// is . - /// - public void Add (Cookie cookie) - { - if (cookie == null) - throw new ArgumentNullException ("cookie"); - - int pos = SearchCookie (cookie); - if (pos == -1) - list.Add (cookie); - else - list [pos] = cookie; - } - - /// - /// Add the elements of the specified to the current . - /// - /// - /// A to add to the current . - /// - /// - /// 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 to the specified , - /// starting at the specified in the . - /// - /// - /// An is the destination of the elements copied from the . - /// - /// - /// An that indicates the zero-based index in at which copying begins. - /// - /// - /// is . - /// - /// - /// is less than zero. - /// - /// - /// - /// is multidimensional. - /// - /// - /// -or- - /// - /// - /// The number of elements in the is greater than the available space - /// from index to the end of the destination . - /// - /// - /// - /// The elements in the 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", "Must not be less than zero."); - - if (array.Rank > 1) - throw new ArgumentException ("Must not be 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."); - - (list as IList).CopyTo (array, index); - } - - /// - /// Copies the elements of the to the specified array of , - /// starting at the specified in the . - /// - /// - /// An array of is the destination of the elements copied from the . - /// - /// - /// An that indicates the zero-based index in at which copying begins. - /// - /// - /// is . - /// - /// - /// is less than zero. - /// - /// - /// The number of elements in the is greater than the available space - /// from index 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", "Must not be 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 to use to iterate through the . - /// - /// - /// An instance of an implementation of the interface - /// to use to iterate through the . - /// - public IEnumerator GetEnumerator () - { - return list.GetEnumerator (); - } - - #endregion - } +namespace WebSocketSharp.Net +{ + /// + /// Provides a collection container for instances of the class. + /// + [Serializable] + public class CookieCollection : ICollection, IEnumerable + { + #region Private Static Fields + + private static CookieCollectionComparer _comparer = new CookieCollectionComparer (); + + #endregion + + #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 { + return from cookie in _list + orderby cookie.Version, + cookie.Name, + cookie.Path.Length descending + select cookie; + } + } + + #endregion + + #region Public Properties + + /// + /// Gets the number of cookies contained in the . + /// + /// + /// An that represents the number of cookies contained in + /// the . + /// + public int Count { + get { + return _list.Count; + } + } + + /// + /// Gets a value indicating whether the is read-only. + /// + /// + /// true if the 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 is + /// thread safe. + /// + /// + /// true if the access to the is thread safe; + /// otherwise, false. The default value is false. + /// + public bool IsSynchronized { + get { + return false; + } + } + + /// + /// Gets the with the specified from + /// the . + /// + /// + /// A with the specified in + /// the . + /// + /// + /// An that represents the zero-based index of the + /// to find. + /// + /// + /// isn't between zero and . + /// + 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 . + /// + /// + /// A with the specified in + /// the . + /// + /// + /// 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 to use to synchronize access to the . + /// + /// + /// An to use to synchronize access to the . + /// + public Object SyncRoot { + get { + return _sync ?? (_sync = new object ()); + } + } + + #endregion + + #region Private Methods + + private static CookieCollection parseRequest (string value) + { + var cookies = new CookieCollection (); + + Cookie cookie = null; + var version = 0; + var pairs = split (value).ToArray (); + 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 = split (value).ToArray (); + 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 IEnumerable split (string value) + { + return value.SplitHeaderValue (',', ';'); + } + + #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 > 0) + _list.Sort (_comparer); + } + + #endregion + + #region Public Methods + + /// + /// Add the specified to the . + /// + /// + /// A to add to the . + /// + /// + /// 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; + } + + /// + /// Add the elements of the specified to the current + /// . + /// + /// + /// A to add to the current . + /// + /// + /// 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 to the specified + /// , starting at the specified in + /// the . + /// + /// + /// An is the destination of the elements copied from + /// the . + /// + /// + /// 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 is greater than the available + /// space from index to the end of the destination . + /// + /// + /// + /// The elements in the 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", "Must not be less than zero."); + + if (array.Rank > 1) + throw new ArgumentException ("Must not be 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."); + + (_list as IList).CopyTo (array, index); + } + + /// + /// Copies the elements of the to the specified array of + /// , starting at the specified in the + /// . + /// + /// + /// An array of is the destination of the elements copied from the + /// . + /// + /// + /// An that represents the zero-based index in + /// at which copying begins. + /// + /// + /// is . + /// + /// + /// is less than zero. + /// + /// + /// The number of elements in the is greater than the available + /// space from index 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", "Must not be 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 to use to iterate through the . + /// + /// + /// A instance to use to iterate through + /// the . + /// + public IEnumerator GetEnumerator () + { + return _list.GetEnumerator (); + } + + #endregion + } } diff --git a/websocket-sharp/Net/CookieCollectionComparer.cs b/websocket-sharp/Net/CookieCollectionComparer.cs new file mode 100644 index 00000000..29109a1a --- /dev/null +++ b/websocket-sharp/Net/CookieCollectionComparer.cs @@ -0,0 +1,50 @@ +#region License +/* + * CookieCollectionComparer.cs + * + * This code is a separation from CookieCollection.cs. + * + * The MIT License + * + * Copyright (c) 2004,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 + +using System; +using System.Collections.Generic; + +namespace WebSocketSharp.Net +{ + internal sealed class CookieCollectionComparer : IComparer + { + public int Compare (Cookie x, Cookie y) + { + if (x == null || y == null) + return 0; + + var c1 = x.Name.Length + x.Value.Length; + var c2 = y.Name.Length + y.Value.Length; + + return c1 - c2; + } + } +} diff --git a/websocket-sharp/websocket-sharp.csproj b/websocket-sharp/websocket-sharp.csproj index 7ade0e6d..63d167ac 100644 --- a/websocket-sharp/websocket-sharp.csproj +++ b/websocket-sharp/websocket-sharp.csproj @@ -130,6 +130,7 @@ +