mirror of
https://gitee.com/jisol/jisol-game/
synced 2025-09-27 02:36:14 +00:00
提交Unity 联机Pro
This commit is contained in:
377
JNFrame2/Assets/Plugins/Best HTTP/Source/Cookies/Cookie.cs
Normal file
377
JNFrame2/Assets/Plugins/Best HTTP/Source/Cookies/Cookie.cs
Normal file
@@ -0,0 +1,377 @@
|
||||
#if !BESTHTTP_DISABLE_COOKIES
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using BestHTTP.Extensions;
|
||||
using System.IO;
|
||||
|
||||
namespace BestHTTP.Cookies
|
||||
{
|
||||
/// <summary>
|
||||
/// The Cookie implementation based on RFC 6265(http://tools.ietf.org/html/rfc6265).
|
||||
/// </summary>
|
||||
public sealed class Cookie : IComparable<Cookie>, IEquatable<Cookie>
|
||||
{
|
||||
private const int Version = 1;
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// The name of the cookie.
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value of the cookie.
|
||||
/// </summary>
|
||||
public string Value { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Date when the Cookie is registered.
|
||||
/// </summary>
|
||||
public DateTime Date { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// When this Cookie last used in a request.
|
||||
/// </summary>
|
||||
public DateTime LastAccess { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Expires attribute indicates the maximum lifetime of the cookie, represented as the date and time at which the cookie expires.
|
||||
/// The user agent is not required to retain the cookie until the specified date has passed.
|
||||
/// In fact, user agents often evict cookies due to memory pressure or privacy concerns.
|
||||
/// </summary>
|
||||
public DateTime Expires { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Max-Age attribute indicates the maximum lifetime of the cookie, represented as the number of seconds until the cookie expires.
|
||||
/// The user agent is not required to retain the cookie for the specified duration.
|
||||
/// In fact, user agents often evict cookies due to memory pressure or privacy concerns.
|
||||
/// </summary>
|
||||
public long MaxAge { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// If a cookie has neither the Max-Age nor the Expires attribute, the user agent will retain the cookie until "the current session is over".
|
||||
/// </summary>
|
||||
public bool IsSession { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Domain attribute specifies those hosts to which the cookie will be sent.
|
||||
/// For example, if the value of the Domain attribute is "example.com", the user agent will include the cookie
|
||||
/// in the Cookie header when making HTTP requests to example.com, www.example.com, and www.corp.example.com.
|
||||
/// If the server omits the Domain attribute, the user agent will return the cookie only to the origin server.
|
||||
/// </summary>
|
||||
public string Domain { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The scope of each cookie is limited to a set of paths, controlled by the Path attribute.
|
||||
/// If the server omits the Path attribute, the user agent will use the "directory" of the request-uri's path component as the default value.
|
||||
/// </summary>
|
||||
public string Path { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Secure attribute limits the scope of the cookie to "secure" channels (where "secure" is defined by the user agent).
|
||||
/// When a cookie has the Secure attribute, the user agent will include the cookie in an HTTP request only if the request is
|
||||
/// transmitted over a secure channel (typically HTTP over Transport Layer Security (TLS)).
|
||||
/// </summary>
|
||||
public bool IsSecure { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The HttpOnly attribute limits the scope of the cookie to HTTP requests.
|
||||
/// In particular, the attribute instructs the user agent to omit the cookie when providing access to
|
||||
/// cookies via "non-HTTP" APIs (such as a web browser API that exposes cookies to scripts).
|
||||
/// </summary>
|
||||
public bool IsHttpOnly { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// SameSite prevents the browser from sending this cookie along with cross-site requests.
|
||||
/// The main goal is mitigate the risk of cross-origin information leakage.
|
||||
/// It also provides some protection against cross-site request forgery attacks. Possible values for the flag are lax or strict.
|
||||
/// <seealso cref="https://web.dev/samesite-cookies-explained/"/>
|
||||
/// </summary>
|
||||
public string SameSite { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Constructors
|
||||
|
||||
public Cookie(string name, string value)
|
||||
:this(name, value, "/", string.Empty)
|
||||
{}
|
||||
|
||||
public Cookie(string name, string value, string path)
|
||||
: this(name, value, path, string.Empty)
|
||||
{}
|
||||
|
||||
public Cookie(string name, string value, string path, string domain)
|
||||
:this() // call the parameter-less constructor to set default values
|
||||
{
|
||||
this.Name = name;
|
||||
this.Value = value;
|
||||
this.Path = path;
|
||||
this.Domain = domain;
|
||||
}
|
||||
|
||||
public Cookie(Uri uri, string name, string value, DateTime expires, bool isSession = true)
|
||||
:this(name, value, uri.AbsolutePath, uri.Host)
|
||||
{
|
||||
this.Expires = expires;
|
||||
this.IsSession = isSession;
|
||||
this.Date = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public Cookie(Uri uri, string name, string value, long maxAge = -1, bool isSession = true)
|
||||
:this(name, value, uri.AbsolutePath, uri.Host)
|
||||
{
|
||||
this.MaxAge = maxAge;
|
||||
this.IsSession = isSession;
|
||||
this.Date = DateTime.UtcNow;
|
||||
this.SameSite = "none";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
internal Cookie()
|
||||
{
|
||||
// If a cookie has neither the Max-Age nor the Expires attribute, the user agent will retain the cookie
|
||||
// until "the current session is over" (as defined by the user agent).
|
||||
IsSession = true;
|
||||
MaxAge = -1;
|
||||
LastAccess = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public bool WillExpireInTheFuture()
|
||||
{
|
||||
// No Expires or Max-Age value sent from the server, we will fake the return value so we will not delete the newly came Cookie
|
||||
if (IsSession)
|
||||
return true;
|
||||
|
||||
// If a cookie has both the Max-Age and the Expires attribute, the Max-Age attribute has precedence and controls the expiration date of the cookie.
|
||||
return MaxAge != -1 ?
|
||||
Math.Max(0, (long)(DateTime.UtcNow - Date).TotalSeconds) < MaxAge :
|
||||
Expires > DateTime.UtcNow;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Guess the storage size of the cookie.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public uint GuessSize()
|
||||
{
|
||||
return (uint)((this.Name != null ? this.Name.Length * sizeof(char) : 0) +
|
||||
(this.Value != null ? this.Value.Length * sizeof(char) : 0) +
|
||||
(this.Domain != null ? this.Domain.Length * sizeof(char) : 0) +
|
||||
(this.Path != null ? this.Path.Length * sizeof(char) : 0) +
|
||||
(this.SameSite != null ? this.SameSite.Length * sizeof(char) : 0) +
|
||||
(sizeof(long) * 4) +
|
||||
(sizeof(bool) * 3));
|
||||
}
|
||||
|
||||
public static Cookie Parse(string header, Uri defaultDomain, Logger.LoggingContext context)
|
||||
{
|
||||
Cookie cookie = new Cookie();
|
||||
try
|
||||
{
|
||||
var kvps = ParseCookieHeader(header);
|
||||
|
||||
foreach (var kvp in kvps)
|
||||
{
|
||||
switch (kvp.Key.ToLowerInvariant())
|
||||
{
|
||||
case "path":
|
||||
// If the attribute-value is empty or if the first character of the attribute-value is not %x2F ("/"):
|
||||
// Let cookie-path be the default-path.
|
||||
cookie.Path = string.IsNullOrEmpty(kvp.Value) || !kvp.Value.StartsWith("/") ? "/" : cookie.Path = kvp.Value;
|
||||
break;
|
||||
|
||||
case "domain":
|
||||
// If the attribute-value is empty, the behavior is undefined. However, the user agent SHOULD ignore the cookie-av entirely.
|
||||
if (string.IsNullOrEmpty(kvp.Value))
|
||||
return null;
|
||||
|
||||
// If the first character of the attribute-value string is %x2E ("."):
|
||||
// Let cookie-domain be the attribute-value without the leading %x2E (".") character.
|
||||
cookie.Domain = kvp.Value.StartsWith(".") ? kvp.Value.Substring(1) : kvp.Value;
|
||||
break;
|
||||
|
||||
case "expires":
|
||||
cookie.Expires = kvp.Value.ToDateTime(DateTime.FromBinary(0));
|
||||
cookie.IsSession = false;
|
||||
break;
|
||||
|
||||
case "max-age":
|
||||
cookie.MaxAge = kvp.Value.ToInt64(-1);
|
||||
cookie.IsSession = false;
|
||||
break;
|
||||
|
||||
case "secure":
|
||||
cookie.IsSecure = true;
|
||||
break;
|
||||
|
||||
case "httponly":
|
||||
cookie.IsHttpOnly = true;
|
||||
break;
|
||||
|
||||
case "samesite":
|
||||
cookie.SameSite = kvp.Value;
|
||||
break;
|
||||
|
||||
default:
|
||||
// check whether name is already set to avoid overwriting it with a non-listed setting
|
||||
if (string.IsNullOrEmpty(cookie.Name))
|
||||
{
|
||||
cookie.Name = kvp.Key;
|
||||
cookie.Value = kvp.Value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Some user agents provide users the option of preventing persistent storage of cookies across sessions.
|
||||
// When configured thusly, user agents MUST treat all received cookies as if the persistent-flag were set to false.
|
||||
if (HTTPManager.EnablePrivateBrowsing)
|
||||
cookie.IsSession = true;
|
||||
|
||||
// http://tools.ietf.org/html/rfc6265#section-4.1.2.3
|
||||
// WARNING: Some existing user agents treat an absent Domain attribute as if the Domain attribute were present and contained the current host name.
|
||||
// For example, if example.com returns a Set-Cookie header without a Domain attribute, these user agents will erroneously send the cookie to www.example.com as well.
|
||||
if (string.IsNullOrEmpty(cookie.Domain))
|
||||
cookie.Domain = defaultDomain.Host;
|
||||
|
||||
// http://tools.ietf.org/html/rfc6265#section-5.3 section 7:
|
||||
// If the cookie-attribute-list contains an attribute with an attribute-name of "Path",
|
||||
// set the cookie's path to attribute-value of the last attribute in the cookie-attribute-list with an attribute-name of "Path".
|
||||
// __Otherwise, set the cookie's path to the default-path of the request-uri.__
|
||||
if (string.IsNullOrEmpty(cookie.Path))
|
||||
cookie.Path = defaultDomain.AbsolutePath;
|
||||
|
||||
cookie.Date = cookie.LastAccess = DateTime.UtcNow;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HTTPManager.Logger.Warning("Cookie", "Parse - Couldn't parse header: " + header + " exception: " + ex.ToString() + " " + ex.StackTrace, context);
|
||||
}
|
||||
return cookie;
|
||||
}
|
||||
|
||||
#region Save & Load
|
||||
|
||||
internal void SaveTo(BinaryWriter stream)
|
||||
{
|
||||
stream.Write(Version);
|
||||
stream.Write(Name ?? string.Empty);
|
||||
stream.Write(Value ?? string.Empty);
|
||||
stream.Write(Date.ToBinary());
|
||||
stream.Write(LastAccess.ToBinary());
|
||||
stream.Write(Expires.ToBinary());
|
||||
stream.Write(MaxAge);
|
||||
stream.Write(IsSession);
|
||||
stream.Write(Domain ?? string.Empty);
|
||||
stream.Write(Path ?? string.Empty);
|
||||
stream.Write(IsSecure);
|
||||
stream.Write(IsHttpOnly);
|
||||
}
|
||||
|
||||
internal void LoadFrom(BinaryReader stream)
|
||||
{
|
||||
/*int version = */stream.ReadInt32();
|
||||
this.Name = stream.ReadString();
|
||||
this.Value = stream.ReadString();
|
||||
this.Date = DateTime.FromBinary(stream.ReadInt64());
|
||||
this.LastAccess = DateTime.FromBinary(stream.ReadInt64());
|
||||
this.Expires = DateTime.FromBinary(stream.ReadInt64());
|
||||
this.MaxAge = stream.ReadInt64();
|
||||
this.IsSession = stream.ReadBoolean();
|
||||
this.Domain = stream.ReadString();
|
||||
this.Path = stream.ReadString();
|
||||
this.IsSecure = stream.ReadBoolean();
|
||||
this.IsHttpOnly = stream.ReadBoolean();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides and new Equals function
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Concat(this.Name, "=", this.Value);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return false;
|
||||
|
||||
return this.Equals(obj as Cookie);
|
||||
}
|
||||
|
||||
public bool Equals(Cookie cookie)
|
||||
{
|
||||
if (cookie == null)
|
||||
return false;
|
||||
|
||||
if (Object.ReferenceEquals(this, cookie))
|
||||
return true;
|
||||
|
||||
return this.Name.Equals(cookie.Name, StringComparison.Ordinal) &&
|
||||
((this.Domain == null && cookie.Domain == null) || this.Domain.Equals(cookie.Domain, StringComparison.Ordinal)) &&
|
||||
((this.Path == null && cookie.Path == null) || this.Path.Equals(cookie.Path, StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return this.ToString().GetHashCode();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Helper Functions
|
||||
|
||||
private static string ReadValue(string str, ref int pos)
|
||||
{
|
||||
string result = string.Empty;
|
||||
if (str == null)
|
||||
return result;
|
||||
|
||||
return str.Read(ref pos, ';');
|
||||
}
|
||||
|
||||
private static List<HeaderValue> ParseCookieHeader(string str)
|
||||
{
|
||||
List<HeaderValue> result = new List<HeaderValue>();
|
||||
|
||||
if (str == null)
|
||||
return result;
|
||||
|
||||
int idx = 0;
|
||||
|
||||
// process the rest of the text
|
||||
while (idx < str.Length)
|
||||
{
|
||||
// Read key
|
||||
string key = str.Read(ref idx, (ch) => ch != '=' && ch != ';').Trim();
|
||||
HeaderValue qp = new HeaderValue(key);
|
||||
|
||||
if (idx < str.Length && str[idx - 1] == '=')
|
||||
qp.Value = ReadValue(str, ref idx);
|
||||
|
||||
result.Add(qp);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IComparable<Cookie> implementation
|
||||
|
||||
public int CompareTo(Cookie other)
|
||||
{
|
||||
return this.LastAccess.CompareTo(other.LastAccess);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c89f8c2ca77c56448d03216927e0546
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
538
JNFrame2/Assets/Plugins/Best HTTP/Source/Cookies/CookieJar.cs
Normal file
538
JNFrame2/Assets/Plugins/Best HTTP/Source/Cookies/CookieJar.cs
Normal file
@@ -0,0 +1,538 @@
|
||||
#if !BESTHTTP_DISABLE_COOKIES
|
||||
|
||||
using BestHTTP.Core;
|
||||
using BestHTTP.PlatformSupport.FileSystem;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace BestHTTP.Cookies
|
||||
{
|
||||
/// <summary>
|
||||
/// The Cookie Jar implementation based on RFC 6265(http://tools.ietf.org/html/rfc6265).
|
||||
/// </summary>
|
||||
public static class CookieJar
|
||||
{
|
||||
// Version of the cookie store. It may be used in a future version for maintaining compatibility.
|
||||
private const int Version = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if File apis are supported.
|
||||
/// </summary>
|
||||
public static bool IsSavingSupported
|
||||
{
|
||||
get
|
||||
{
|
||||
#if !BESTHTTP_DISABLE_COOKIE_SAVE
|
||||
if (IsSupportCheckDone)
|
||||
return _isSavingSupported;
|
||||
|
||||
try
|
||||
{
|
||||
#if UNITY_WEBGL && !UNITY_EDITOR
|
||||
_isSavingSupported = false;
|
||||
#else
|
||||
HTTPManager.IOService.DirectoryExists(HTTPManager.GetRootCacheFolder());
|
||||
_isSavingSupported = true;
|
||||
#endif
|
||||
}
|
||||
catch
|
||||
{
|
||||
_isSavingSupported = false;
|
||||
|
||||
HTTPManager.Logger.Warning("CookieJar", "Cookie saving and loading disabled!");
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsSupportCheckDone = true;
|
||||
}
|
||||
|
||||
return _isSavingSupported;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The plugin will delete cookies that are accessed this threshold ago. Its default value is 7 days.
|
||||
/// </summary>
|
||||
public static TimeSpan AccessThreshold = TimeSpan.FromDays(7);
|
||||
|
||||
#region Privates
|
||||
|
||||
/// <summary>
|
||||
/// List of the Cookies
|
||||
/// </summary>
|
||||
private static List<Cookie> Cookies = new List<Cookie>();
|
||||
private static string CookieFolder { get; set; }
|
||||
private static string LibraryPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Synchronization object for thread safety.
|
||||
/// </summary>
|
||||
private static ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
|
||||
|
||||
#if !BESTHTTP_DISABLE_COOKIE_SAVE
|
||||
private static bool _isSavingSupported;
|
||||
private static bool IsSupportCheckDone;
|
||||
#endif
|
||||
|
||||
private static bool Loaded;
|
||||
#endregion
|
||||
|
||||
#region Internal Functions
|
||||
|
||||
internal static void SetupFolder()
|
||||
{
|
||||
#if !BESTHTTP_DISABLE_COOKIE_SAVE
|
||||
if (!CookieJar.IsSavingSupported)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(CookieFolder) || string.IsNullOrEmpty(LibraryPath))
|
||||
{
|
||||
CookieFolder = System.IO.Path.Combine(HTTPManager.GetRootCacheFolder(), "Cookies");
|
||||
LibraryPath = System.IO.Path.Combine(CookieFolder, "Library");
|
||||
}
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will set or update all cookies from the response object.
|
||||
/// </summary>
|
||||
internal static bool Set(HTTPResponse response)
|
||||
{
|
||||
if (response == null)
|
||||
return false;
|
||||
|
||||
List<Cookie> newCookies = new List<Cookie>();
|
||||
var setCookieHeaders = response.GetHeaderValues("set-cookie");
|
||||
|
||||
// No cookies. :'(
|
||||
if (setCookieHeaders == null)
|
||||
return false;
|
||||
|
||||
foreach (var cookieHeader in setCookieHeaders)
|
||||
{
|
||||
Cookie cookie = Cookie.Parse(cookieHeader, response.baseRequest.CurrentUri, response.baseRequest.Context);
|
||||
if (cookie != null)
|
||||
{
|
||||
rwLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
int idx;
|
||||
var old = Find(cookie, out idx);
|
||||
|
||||
// if no value for the cookie or already expired then the server asked us to delete the cookie
|
||||
bool expired = string.IsNullOrEmpty(cookie.Value) || !cookie.WillExpireInTheFuture();
|
||||
|
||||
if (!expired)
|
||||
{
|
||||
// no old cookie, add it straight to the list
|
||||
if (old == null)
|
||||
{
|
||||
Cookies.Add(cookie);
|
||||
|
||||
newCookies.Add(cookie);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update the creation-time of the newly created cookie to match the creation-time of the old-cookie.
|
||||
cookie.Date = old.Date;
|
||||
Cookies[idx] = cookie;
|
||||
|
||||
newCookies.Add(cookie);
|
||||
}
|
||||
}
|
||||
else if (idx != -1) // delete the cookie
|
||||
Cookies.RemoveAt(idx);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore cookie on error
|
||||
}
|
||||
finally
|
||||
{
|
||||
rwLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response.Cookies = newCookies;
|
||||
|
||||
PluginEventHelper.EnqueuePluginEvent(new PluginEventInfo(PluginEvents.SaveCookieLibrary));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all expired or 'old' cookies, and will keep the sum size of cookies under the given size.
|
||||
/// </summary>
|
||||
internal static void Maintain(bool sendEvent)
|
||||
{
|
||||
// It's not the same as in the rfc:
|
||||
// http://tools.ietf.org/html/rfc6265#section-5.3
|
||||
|
||||
rwLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
uint size = 0;
|
||||
|
||||
for (int i = 0; i < Cookies.Count; )
|
||||
{
|
||||
var cookie = Cookies[i];
|
||||
|
||||
// Remove expired or not used cookies
|
||||
if (!cookie.WillExpireInTheFuture() || (cookie.LastAccess + AccessThreshold) < DateTime.UtcNow)
|
||||
{
|
||||
Cookies.RemoveAt(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!cookie.IsSession)
|
||||
size += cookie.GuessSize();
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (size > HTTPManager.CookieJarSize)
|
||||
{
|
||||
Cookies.Sort();
|
||||
|
||||
while (size > HTTPManager.CookieJarSize && Cookies.Count > 0)
|
||||
{
|
||||
var cookie = Cookies[0];
|
||||
Cookies.RemoveAt(0);
|
||||
|
||||
size -= cookie.GuessSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
finally
|
||||
{
|
||||
rwLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
if (sendEvent)
|
||||
PluginEventHelper.EnqueuePluginEvent(new PluginEventInfo(PluginEvents.SaveCookieLibrary));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the Cookie Jar to a file.
|
||||
/// </summary>
|
||||
/// <remarks>Not implemented under Unity WebPlayer</remarks>
|
||||
internal static void Persist()
|
||||
{
|
||||
#if !BESTHTTP_DISABLE_COOKIE_SAVE
|
||||
if (!IsSavingSupported)
|
||||
return;
|
||||
|
||||
if (!Loaded)
|
||||
return;
|
||||
|
||||
// Delete any expired cookie
|
||||
Maintain(false);
|
||||
|
||||
rwLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
if (!HTTPManager.IOService.DirectoryExists(CookieFolder))
|
||||
HTTPManager.IOService.DirectoryCreate(CookieFolder);
|
||||
|
||||
using (var fs = HTTPManager.IOService.CreateFileStream(LibraryPath, FileStreamModes.Create))
|
||||
using (var bw = new System.IO.BinaryWriter(fs))
|
||||
{
|
||||
bw.Write(Version);
|
||||
|
||||
// Count how many non-session cookies we have
|
||||
int count = 0;
|
||||
foreach (var cookie in Cookies)
|
||||
if (!cookie.IsSession)
|
||||
count++;
|
||||
|
||||
bw.Write(count);
|
||||
|
||||
// Save only the persistable cookies
|
||||
foreach (var cookie in Cookies)
|
||||
if (!cookie.IsSession)
|
||||
cookie.SaveTo(bw);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
finally
|
||||
{
|
||||
rwLock.ExitWriteLock();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load previously persisted cookie library from the file.
|
||||
/// </summary>
|
||||
internal static void Load()
|
||||
{
|
||||
#if !BESTHTTP_DISABLE_COOKIE_SAVE
|
||||
if (!IsSavingSupported)
|
||||
return;
|
||||
|
||||
if (Loaded)
|
||||
return;
|
||||
|
||||
SetupFolder();
|
||||
|
||||
rwLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
Cookies.Clear();
|
||||
|
||||
if (!HTTPManager.IOService.DirectoryExists(CookieFolder))
|
||||
HTTPManager.IOService.DirectoryCreate(CookieFolder);
|
||||
|
||||
if (!HTTPManager.IOService.FileExists(LibraryPath))
|
||||
return;
|
||||
|
||||
using (var fs = HTTPManager.IOService.CreateFileStream(LibraryPath, FileStreamModes.OpenRead))
|
||||
using (var br = new System.IO.BinaryReader(fs))
|
||||
{
|
||||
/*int version = */br.ReadInt32();
|
||||
int cookieCount = br.ReadInt32();
|
||||
|
||||
for (int i = 0; i < cookieCount; ++i)
|
||||
{
|
||||
Cookie cookie = new Cookie();
|
||||
cookie.LoadFrom(br);
|
||||
|
||||
if (cookie.WillExpireInTheFuture())
|
||||
Cookies.Add(cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
Cookies.Clear();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Loaded = true;
|
||||
rwLock.ExitWriteLock();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Functions
|
||||
|
||||
/// <summary>
|
||||
/// Returns all Cookies that corresponds to the given Uri.
|
||||
/// </summary>
|
||||
public static List<Cookie> Get(Uri uri)
|
||||
{
|
||||
Load();
|
||||
|
||||
rwLock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
List<Cookie> result = null;
|
||||
|
||||
for (int i = 0; i < Cookies.Count; ++i)
|
||||
{
|
||||
Cookie cookie = Cookies[i];
|
||||
if (cookie.WillExpireInTheFuture() && (uri.Host.IndexOf(cookie.Domain) != -1 || string.Format("{0}:{1}", uri.Host, uri.Port).IndexOf(cookie.Domain) != -1) && uri.AbsolutePath.StartsWith(cookie.Path))
|
||||
{
|
||||
if (result == null)
|
||||
result = new List<Cookie>();
|
||||
|
||||
result.Add(cookie);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
rwLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will add a new, or overwrite an old cookie if already exists.
|
||||
/// </summary>
|
||||
public static void Set(Uri uri, Cookie cookie)
|
||||
{
|
||||
Set(cookie);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will add a new, or overwrite an old cookie if already exists.
|
||||
/// </summary>
|
||||
public static void Set(Cookie cookie)
|
||||
{
|
||||
Load();
|
||||
|
||||
rwLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
int idx;
|
||||
Find(cookie, out idx);
|
||||
|
||||
if (idx >= 0)
|
||||
Cookies[idx] = cookie;
|
||||
else
|
||||
Cookies.Add(cookie);
|
||||
}
|
||||
finally
|
||||
{
|
||||
rwLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
PluginEventHelper.EnqueuePluginEvent(new PluginEventInfo(PluginEvents.SaveCookieLibrary));
|
||||
}
|
||||
|
||||
public static List<Cookie> GetAll()
|
||||
{
|
||||
Load();
|
||||
|
||||
return Cookies;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all cookies from the Jar.
|
||||
/// </summary>
|
||||
public static void Clear()
|
||||
{
|
||||
Load();
|
||||
|
||||
rwLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
Cookies.Clear();
|
||||
}
|
||||
finally
|
||||
{
|
||||
rwLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
Persist();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes cookies that older than the given parameter.
|
||||
/// </summary>
|
||||
public static void Clear(TimeSpan olderThan)
|
||||
{
|
||||
Load();
|
||||
|
||||
rwLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < Cookies.Count; )
|
||||
{
|
||||
var cookie = Cookies[i];
|
||||
|
||||
// Remove expired or not used cookies
|
||||
if (!cookie.WillExpireInTheFuture() || (cookie.Date + olderThan) < DateTime.UtcNow)
|
||||
Cookies.RemoveAt(i);
|
||||
else
|
||||
i++;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
rwLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
Persist();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes cookies that matches to the given domain.
|
||||
/// </summary>
|
||||
public static void Clear(string domain)
|
||||
{
|
||||
Load();
|
||||
|
||||
rwLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < Cookies.Count; )
|
||||
{
|
||||
var cookie = Cookies[i];
|
||||
|
||||
// Remove expired or not used cookies
|
||||
if (!cookie.WillExpireInTheFuture() || cookie.Domain.IndexOf(domain) != -1)
|
||||
Cookies.RemoveAt(i);
|
||||
else
|
||||
i++;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
rwLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
Persist();
|
||||
}
|
||||
|
||||
public static void Remove(Uri uri, string name)
|
||||
{
|
||||
Load();
|
||||
|
||||
rwLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < Cookies.Count; )
|
||||
{
|
||||
var cookie = Cookies[i];
|
||||
|
||||
if (cookie.Name.Equals(name, StringComparison.OrdinalIgnoreCase) && uri.Host.IndexOf(cookie.Domain) != -1)
|
||||
Cookies.RemoveAt(i);
|
||||
else
|
||||
i++;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
rwLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
PluginEventHelper.EnqueuePluginEvent(new PluginEventInfo(PluginEvents.SaveCookieLibrary));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Helper Functions
|
||||
|
||||
/// <summary>
|
||||
/// Find and return a Cookie and his index in the list.
|
||||
/// </summary>
|
||||
private static Cookie Find(Cookie cookie, out int idx)
|
||||
{
|
||||
for (int i = 0; i < Cookies.Count; ++i)
|
||||
{
|
||||
Cookie c = Cookies[i];
|
||||
|
||||
if (c.Equals(cookie))
|
||||
{
|
||||
idx = i;
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
idx = -1;
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b6b22cd983735ee46908eac3f37eb798
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user