Refactored CookieCollection.cs

This commit is contained in:
sta 2014-03-24 21:02:06 +09:00
parent 6eead5d317
commit fefae5b72f
3 changed files with 639 additions and 581 deletions

View File

@ -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 <loz@cable.a2000.nl>
* - Gonzalo Paniagua Javier <gonzalo@ximian.com>
* - Sebastien Pouliot <sebastien@ximian.com>
*/
#endregion
using System;
using System.Collections;
@ -39,39 +47,24 @@ using System.Linq;
using System.Runtime.Serialization;
using System.Text;
namespace WebSocketSharp.Net {
namespace WebSocketSharp.Net
{
/// <summary>
/// Provides a collection container for instances of the <see cref="Cookie"/> class.
/// </summary>
[Serializable]
public class CookieCollection : ICollection, IEnumerable
{
// not 100% identical to MS implementation
sealed class CookieCollectionComparer : IComparer<Cookie>
{
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 ();
private static CookieCollectionComparer _comparer = new CookieCollectionComparer ();
#endregion
#region Private Fields
List<Cookie> list;
object sync;
private List<Cookie> _list;
private object _sync;
#endregion
@ -82,7 +75,7 @@ namespace WebSocketSharp.Net {
/// </summary>
public CookieCollection ()
{
list = new List<Cookie> ();
_list = new List<Cookie> ();
}
#endregion
@ -90,12 +83,14 @@ namespace WebSocketSharp.Net {
#region Internal Properties
internal IList<Cookie> List {
get { return list; }
get {
return _list;
}
}
internal IEnumerable<Cookie> Sorted {
get {
return from cookie in list
return from cookie in _list
orderby cookie.Version,
cookie.Name,
cookie.Path.Length descending
@ -111,68 +106,78 @@ namespace WebSocketSharp.Net {
/// Gets the number of cookies contained in the <see cref="CookieCollection"/>.
/// </summary>
/// <value>
/// An <see cref="int"/> that indicates the number of cookies contained in the <see cref="CookieCollection"/>.
/// An <see cref="int"/> that represents the number of cookies contained in
/// the <see cref="CookieCollection"/>.
/// </value>
public int Count {
get { return list.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.
//
/// <summary>
/// Gets a value indicating whether the <see cref="CookieCollection"/> is read-only.
/// </summary>
/// <value>
/// <c>true</c> if the <see cref="CookieCollection"/> is read-only; otherwise, <c>false</c>.
/// The default is <c>true</c>.
/// The default value is <c>true</c>.
/// </value>
public bool IsReadOnly {
get { return true; }
// 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;
}
}
/// <summary>
/// Gets a value indicating whether access to the <see cref="CookieCollection"/> is thread safe.
/// Gets a value indicating whether the access to the <see cref="CookieCollection"/> is
/// thread safe.
/// </summary>
/// <value>
/// <c>true</c> if access to the <see cref="CookieCollection"/> is thread safe; otherwise, <c>false</c>.
/// The default is <c>false</c>.
/// <c>true</c> if the access to the <see cref="CookieCollection"/> is thread safe;
/// otherwise, <c>false</c>. The default value is <c>false</c>.
/// </value>
public bool IsSynchronized {
get { return false; }
get {
return false;
}
}
/// <summary>
/// Gets the <see cref="Cookie"/> with the specified <paramref name="index"/> from the <see cref="CookieCollection"/>.
/// Gets the <see cref="Cookie"/> with the specified <paramref name="index"/> from
/// the <see cref="CookieCollection"/>.
/// </summary>
/// <value>
/// A <see cref="Cookie"/> with the specified <paramref name="index"/> in the <see cref="CookieCollection"/>.
/// A <see cref="Cookie"/> with the specified <paramref name="index"/> in
/// the <see cref="CookieCollection"/>.
/// </value>
/// <param name="index">
/// An <see cref="int"/> is the zero-based index of the <see cref="Cookie"/> to find.
/// An <see cref="int"/> that represents the zero-based index of the <see cref="Cookie"/>
/// to find.
/// </param>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="index"/> is less than zero or <paramref name="index"/> is greater than or
/// equal to <see cref="Count"/>.
/// <paramref name="index"/> isn't between zero and <see cref="Count"/>.
/// </exception>
public Cookie this [int index] {
get {
if (index < 0 || index >= list.Count)
if (index < 0 || index >= _list.Count)
throw new ArgumentOutOfRangeException ("index");
return list [index];
return _list [index];
}
}
/// <summary>
/// Gets the <see cref="Cookie"/> with the specified <paramref name="name"/> from the <see cref="CookieCollection"/>.
/// Gets the <see cref="Cookie"/> with the specified <paramref name="name"/> from
/// the <see cref="CookieCollection"/>.
/// </summary>
/// <value>
/// A <see cref="Cookie"/> with the specified <paramref name="name"/> in the <see cref="CookieCollection"/>.
/// A <see cref="Cookie"/> with the specified <paramref name="name"/> in
/// the <see cref="CookieCollection"/>.
/// </value>
/// <param name="name">
/// A <see cref="string"/> is the name of the <see cref="Cookie"/> to find.
/// A <see cref="string"/> that represents the name of the <see cref="Cookie"/> to find.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="name"/> is <see langword="null"/>.
@ -182,10 +187,9 @@ namespace WebSocketSharp.Net {
if (name == null)
throw new ArgumentNullException ("name");
foreach (var cookie in Sorted) {
foreach (var cookie in Sorted)
if (cookie.Name.Equals (name, StringComparison.InvariantCultureIgnoreCase))
return cookie;
}
return null;
}
@ -199,10 +203,7 @@ namespace WebSocketSharp.Net {
/// </value>
public Object SyncRoot {
get {
if (sync == null)
sync = new object ();
return sync;
return _sync ?? (_sync = new object ());
}
}
@ -210,15 +211,15 @@ namespace WebSocketSharp.Net {
#region Private Methods
static CookieCollection ParseRequest (string value)
private static CookieCollection parseRequest (string value)
{
var cookies = new CookieCollection ();
Cookie cookie = null;
int version = 0;
string [] pairs = Split(value).ToArray();
var version = 0;
var pairs = split (value).ToArray ();
for (int i = 0; i < pairs.Length; i++) {
string pair = pairs [i].Trim ();
var pair = pairs [i].Trim ();
if (pair.Length == 0)
continue;
@ -247,7 +248,8 @@ namespace WebSocketSharp.Net {
string name;
string val = String.Empty;
int pos = pair.IndexOf ('=');
var pos = pair.IndexOf ('=');
if (pos == -1) {
name = pair;
}
@ -271,14 +273,14 @@ namespace WebSocketSharp.Net {
return cookies;
}
static CookieCollection ParseResponse (string value)
private static CookieCollection parseResponse (string value)
{
var cookies = new CookieCollection ();
Cookie cookie = null;
string [] pairs = Split(value).ToArray();
var pairs = split (value).ToArray ();
for (int i = 0; i < pairs.Length; i++) {
string pair = pairs [i].Trim ();
var pair = pairs [i].Trim ();
if (pair.Length == 0)
continue;
@ -292,8 +294,9 @@ namespace WebSocketSharp.Net {
buffer.AppendFormat (", {0}", pairs [++i].Trim ());
DateTime expires;
if (!DateTime.TryParseExact (buffer.ToString (),
new string [] { "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'", "r" },
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))
@ -303,7 +306,7 @@ namespace WebSocketSharp.Net {
cookie.Expires = expires.ToLocalTime ();
}
else if (pair.StartsWith ("max-age", StringComparison.InvariantCultureIgnoreCase)) {
int max = Int32.Parse (pair.GetValueInternal ("=").Trim ('"'));
var max = Int32.Parse (pair.GetValueInternal ("=").Trim ('"'));
var expires = DateTime.Now.AddSeconds ((double) max);
if (cookie != null)
cookie.Expires = expires;
@ -350,7 +353,8 @@ namespace WebSocketSharp.Net {
string name;
string val = String.Empty;
int pos = pair.IndexOf ('=');
var pos = pair.IndexOf ('=');
if (pos == -1) {
name = pair;
}
@ -372,34 +376,25 @@ namespace WebSocketSharp.Net {
return cookies;
}
int SearchCookie (Cookie cookie)
private 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;
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;
}
static IEnumerable<string> Split (string value)
private static IEnumerable<string> split (string value)
{
return value.SplitHeaderValue (',', ';');
}
@ -411,23 +406,26 @@ namespace WebSocketSharp.Net {
internal static CookieCollection Parse (string value, bool response)
{
return response
? ParseResponse (value)
: ParseRequest (value);
? parseResponse (value)
: parseRequest (value);
}
internal void SetOrRemove (Cookie cookie)
{
int pos = SearchCookie (cookie);
var pos = searchCookie (cookie);
if (pos == -1) {
if (!cookie.Expired)
list.Add (cookie);
_list.Add (cookie);
return;
}
else {
if (!cookie.Expired)
list [pos] = cookie;
else
list.RemoveAt (pos);
if (!cookie.Expired) {
_list [pos] = cookie;
return;
}
_list.RemoveAt (pos);
}
internal void SetOrRemove (CookieCollection cookies)
@ -438,8 +436,8 @@ namespace WebSocketSharp.Net {
internal void Sort ()
{
if (list.Count > 0)
list.Sort (Comparer);
if (_list.Count > 0)
_list.Sort (_comparer);
}
#endregion
@ -460,15 +458,18 @@ namespace WebSocketSharp.Net {
if (cookie == null)
throw new ArgumentNullException ("cookie");
int pos = SearchCookie (cookie);
if (pos == -1)
list.Add (cookie);
else
list [pos] = cookie;
var pos = searchCookie (cookie);
if (pos == -1) {
_list.Add (cookie);
return;
}
_list [pos] = cookie;
}
/// <summary>
/// Add the elements of the specified <see cref="CookieCollection"/> to the current <see cref="CookieCollection"/>.
/// Add the elements of the specified <see cref="CookieCollection"/> to the current
/// <see cref="CookieCollection"/>.
/// </summary>
/// <param name="cookies">
/// A <see cref="CookieCollection"/> to add to the current <see cref="CookieCollection"/>.
@ -486,14 +487,17 @@ namespace WebSocketSharp.Net {
}
/// <summary>
/// Copies the elements of the <see cref="CookieCollection"/> to the specified <see cref="Array"/>,
/// starting at the specified <paramref name="index"/> in the <paramref name="array"/>.
/// Copies the elements of the <see cref="CookieCollection"/> to the specified
/// <see cref="Array"/>, starting at the specified <paramref name="index"/> in
/// the <paramref name="array"/>.
/// </summary>
/// <param name="array">
/// An <see cref="Array"/> is the destination of the elements copied from the <see cref="CookieCollection"/>.
/// An <see cref="Array"/> is the destination of the elements copied from
/// the <see cref="CookieCollection"/>.
/// </param>
/// <param name="index">
/// An <see cref="int"/> that indicates the zero-based index in <paramref name="array"/> at which copying begins.
/// An <see cref="int"/> that represents the zero-based index in <paramref name="array"/>
/// at which copying begins.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="array"/> is <see langword="null"/>.
@ -509,13 +513,13 @@ namespace WebSocketSharp.Net {
/// -or-
/// </para>
/// <para>
/// The number of elements in the <see cref="CookieCollection"/> is greater than the available space
/// from index to the end of the destination <paramref name="array"/>.
/// The number of elements in the <see cref="CookieCollection"/> is greater than the available
/// space from index to the end of the destination <paramref name="array"/>.
/// </para>
/// </exception>
/// <exception cref="InvalidCastException">
/// The elements in the <see cref="CookieCollection"/> cannot be cast automatically
/// to the type of the destination <paramref name="array"/>.
/// The elements in the <see cref="CookieCollection"/> cannot be cast automatically to the type
/// of the destination <paramref name="array"/>.
/// </exception>
public void CopyTo (Array array, int index)
{
@ -528,7 +532,7 @@ namespace WebSocketSharp.Net {
if (array.Rank > 1)
throw new ArgumentException ("Must not be multidimensional.", "array");
if (array.Length - index < list.Count)
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.");
@ -536,18 +540,21 @@ namespace WebSocketSharp.Net {
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);
(_list as IList).CopyTo (array, index);
}
/// <summary>
/// Copies the elements of the <see cref="CookieCollection"/> to the specified array of <see cref="Cookie"/>,
/// starting at the specified <paramref name="index"/> in the <paramref name="array"/>.
/// Copies the elements of the <see cref="CookieCollection"/> to the specified array of
/// <see cref="Cookie"/>, starting at the specified <paramref name="index"/> in the
/// <paramref name="array"/>.
/// </summary>
/// <param name="array">
/// An array of <see cref="Cookie"/> is the destination of the elements copied from the <see cref="CookieCollection"/>.
/// An array of <see cref="Cookie"/> is the destination of the elements copied from the
/// <see cref="CookieCollection"/>.
/// </param>
/// <param name="index">
/// An <see cref="int"/> that indicates the zero-based index in <paramref name="array"/> at which copying begins.
/// An <see cref="int"/> that represents the zero-based index in <paramref name="array"/>
/// at which copying begins.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="array"/> is <see langword="null"/>.
@ -556,8 +563,8 @@ namespace WebSocketSharp.Net {
/// <paramref name="index"/> is less than zero.
/// </exception>
/// <exception cref="ArgumentException">
/// The number of elements in the <see cref="CookieCollection"/> is greater than the available space
/// from index to the end of the destination <paramref name="array"/>.
/// The number of elements in the <see cref="CookieCollection"/> is greater than the available
/// space from index to the end of the destination <paramref name="array"/>.
/// </exception>
public void CopyTo (Cookie [] array, int index)
{
@ -567,23 +574,23 @@ namespace WebSocketSharp.Net {
if (index < 0)
throw new ArgumentOutOfRangeException ("index", "Must not be less than zero.");
if (array.Length - index < list.Count)
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);
_list.CopyTo (array, index);
}
/// <summary>
/// Gets the enumerator to use to iterate through the <see cref="CookieCollection"/>.
/// </summary>
/// <returns>
/// An instance of an implementation of the <see cref="IEnumerator"/> interface
/// to use to iterate through the <see cref="CookieCollection"/>.
/// A <see cref="IEnumerator"/> instance to use to iterate through
/// the <see cref="CookieCollection"/>.
/// </returns>
public IEnumerator GetEnumerator ()
{
return list.GetEnumerator ();
return _list.GetEnumerator ();
}
#endregion

View File

@ -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<Cookie>
{
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;
}
}
}

View File

@ -130,6 +130,7 @@
<Compile Include="Net\HttpDigestIdentity.cs" />
<Compile Include="Net\NetworkCredential.cs" />
<Compile Include="Server\WebSocketServiceManager.cs" />
<Compile Include="Net\CookieCollectionComparer.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>