This commit is contained in:
2020-07-09 08:50:24 +08:00
parent 13d25f4707
commit c523462b82
1818 changed files with 174940 additions and 582 deletions

8
Assets/BestHTTP.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d1e9dc086ea7b4e13bef69c3dbbb5697
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6c727651d996e49d38ac246cd52c383f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,63 @@
namespace BestHTTP.Authentication
{
/// <summary>
/// Authentication types that supported by BestHTTP.
/// The authentication is defined by the server, so the Basic and Digest are not interchangeable. If you don't know what to use, the preferred way is to choose Unknow.
/// </summary>
public enum AuthenticationTypes
{
/// <summary>
/// If the authentication type is not known this will do a challenge turn to receive what methode should be choosen.
/// </summary>
Unknown,
/// <summary>
/// The most basic authentication type. It's easy to do, and easy to crack. ;)
/// </summary>
Basic,
/// <summary>
///
/// </summary>
Digest
}
/// <summary>
/// Hold all information that required to authenticate to a remote server.
/// </summary>
public sealed class Credentials
{
/// <summary>
/// The type of the Authentication. If you don't know what to use, the preferred way is to choose Unknow.
/// </summary>
public AuthenticationTypes Type { get; private set; }
/// <summary>
/// The username to authenticate on the remote server.
/// </summary>
public string UserName { get; private set; }
/// <summary>
/// The password to use in the authentication process. The password will be stored only in this class.
/// </summary>
public string Password { get; private set; }
/// <summary>
/// Set up the authentication credentials with the username and password. The Type will be set to Unknown.
/// </summary>
public Credentials(string userName, string password)
:this(AuthenticationTypes.Unknown, userName, password)
{
}
/// <summary>
/// Set up the authentication credentials with the given authentication type, username and password.
/// </summary>
public Credentials(AuthenticationTypes type, string userName, string password)
{
this.Type = type;
this.UserName = userName;
this.Password = password;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9a01ccd43db8b4539a69a8cbbb078857
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,272 @@
using System;
using System.Collections.Generic;
namespace BestHTTP.Authentication
{
using BestHTTP.Extensions;
using System.Text;
/// <summary>
/// Internal class that stores all information that received from a server in a WWW-Authenticate and need to construct a valid Authorization header. Based on rfc 2617 (http://tools.ietf.org/html/rfc2617).
/// Used only internally by the plugin.
/// </summary>
internal sealed class Digest
{
#region Public Properties
/// <summary>
/// The Uri that this Digest is bound to.
/// </summary>
public Uri Uri { get; private set; }
public AuthenticationTypes Type { get; private set; }
/// <summary>
/// A string to be displayed to users so they know which username and password to use.
/// This string should contain at least the name of the host performing the authentication and might additionally indicate the collection of users who might have access.
/// </summary>
public string Realm { get; private set; }
/// <summary>
/// A flag, indicating that the previous request from the client was rejected because the nonce value was stale.
/// If stale is TRUE (case-insensitive), the client may wish to simply retry the request with a new encrypted response, without the user for a new username and password.
/// The server should only set stale to TRUE if it receives a request for which the nonce is invalid but with a valid digest for that nonce
/// (indicating that the client knows the correct username/password).
/// If stale is FALSE, or anything other than TRUE, or the stale directive is not present, the username and/or password are invalid, and new values must be obtained.
/// </summary>
public bool Stale { get; private set; }
#endregion
#region Private Properties
/// <summary>
/// A server-specified data string which should be uniquely generated each time a 401 response is made.
/// Specifically, since the string is passed in the header lines as a quoted string, the double-quote character is not allowed.
/// </summary>
private string Nonce { get; set; }
/// <summary>
/// A string of data, specified by the server, which should be returned by the client unchanged in the Authorization header of subsequent requests with URIs in the same protection space.
/// It is recommended that this string be base64 or data.
/// </summary>
private string Opaque { get; set; }
/// <summary>
/// A string indicating a pair of algorithms used to produce the digest and a checksum. If this is not present it is assumed to be "MD5".
/// If the algorithm is not understood, the challenge should be ignored (and a different one used, if there is more than one).
/// </summary>
private string Algorithm { get; set; }
/// <summary>
/// List of URIs, as specified in RFC XURI, that define the protection space.
/// If a URI is an abs_path, it is relative to the canonical root URL (see section 1.2 above) of the server being accessed.
/// An absoluteURI in this list may refer to a different server than the one being accessed.
/// The client can use this list to determine the set of URIs for which the same authentication information may be sent:
/// any URI that has a URI in this list as a prefix (after both have been made absolute) may be assumed to be in the same protection space.
/// If this directive is omitted or its value is empty, the client should assume that the protection space consists of all URIs on the responding server.
/// </summary>
public List<string> ProtectedUris { get; private set; }
/// <summary>
/// If present, it is a quoted string of one or more tokens indicating the "quality of protection" values supported by the server.
/// The value "auth" indicates authentication. The value "auth-int" indicates authentication with integrity protection.
/// </summary>
private string QualityOfProtections { get; set; }
/// <summary>
/// his MUST be specified if a qop directive is sent (see above), and MUST NOT be specified if the server did not send a qop directive in the WWW-Authenticate header field.
/// The nc-value is the hexadecimal count of the number of requests (including the current request) that the client has sent with the nonce value in this request.
/// </summary>
private int NonceCount { get; set; }
/// <summary>
/// Used to store the last HA1 that can be used in the next header generation when Algorithm is set to "md5-sess".
/// </summary>
private string HA1Sess { get; set; }
#endregion
internal Digest(Uri uri)
{
this.Uri = uri;
this.Algorithm = "md5";
}
/// <summary>
/// Parses a WWW-Authenticate header's value to retrive all information.
/// </summary>
public void ParseChallange(string header)
{
// Reset some values to its defaults.
this.Type = AuthenticationTypes.Unknown;
this.Stale = false;
this.Opaque = null;
this.HA1Sess = null;
this.NonceCount = 0;
this.QualityOfProtections = null;
if (this.ProtectedUris != null)
this.ProtectedUris.Clear();
// Parse the header
WWWAuthenticateHeaderParser qpl = new WWWAuthenticateHeaderParser(header);
// Then process
foreach (var qp in qpl.Values)
switch (qp.Key)
{
case "basic": this.Type = AuthenticationTypes.Basic; break;
case "digest": this.Type = AuthenticationTypes.Digest; break;
case "realm": this.Realm = qp.Value; break;
case "domain":
{
if (string.IsNullOrEmpty(qp.Value) || qp.Value.Length == 0)
break;
if (this.ProtectedUris == null)
this.ProtectedUris = new List<string>();
int idx = 0;
string val = qp.Value.Read(ref idx, ' ');
do
{
this.ProtectedUris.Add(val);
val = qp.Value.Read(ref idx, ' ');
} while (idx < qp.Value.Length);
break;
}
case "nonce": this.Nonce = qp.Value; break;
case "qop": this.QualityOfProtections = qp.Value; break;
case "stale": this.Stale = bool.Parse(qp.Value); break;
case "opaque": this.Opaque = qp.Value; break;
case "algorithm": this.Algorithm = qp.Value; break;
}
}
/// <summary>
/// Generates a string that can be set to an Authorization header.
/// </summary>
public string GenerateResponseHeader(HTTPRequest request, Credentials credentials)
{
try
{
switch (Type)
{
case AuthenticationTypes.Basic:
return string.Concat("Basic ", Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Format("{0}:{1}", credentials.UserName, credentials.Password))));
case AuthenticationTypes.Digest:
{
NonceCount++;
string HA1 = string.Empty;
// The cnonce-value is an opaque quoted string value provided by the client and used by both client and server to avoid chosen plaintext attacks, to provide mutual
// authentication, and to provide some message integrity protection.
string cnonce = new System.Random(request.GetHashCode()).Next(int.MinValue, int.MaxValue).ToString("X8");
string ncvalue = NonceCount.ToString("X8");
switch (Algorithm.TrimAndLower())
{
case "md5":
HA1 = string.Format("{0}:{1}:{2}", credentials.UserName, Realm, credentials.Password).CalculateMD5Hash();
break;
case "md5-sess":
if (string.IsNullOrEmpty(this.HA1Sess))
this.HA1Sess = string.Format("{0}:{1}:{2}:{3}:{4}", credentials.UserName, Realm, credentials.Password, Nonce, ncvalue).CalculateMD5Hash();
HA1 = this.HA1Sess;
break;
default: //throw new NotSupportedException("Not supported hash algorithm found in Web Authentication: " + Algorithm);
return string.Empty;
}
// A string of 32 hex digits, which proves that the user knows a password. Set according to the qop value.
string response = string.Empty;
// The server sent QoP-value can be a list of supported methodes(if sent at all - in this case it's null).
// The rfc is not specify that this is a space or comma separeted list. So it can be "auth, auth-int" or "auth auth-int".
// We will first check the longer value("auth-int") then the short one ("auth"). If one matches we will reset the qop to the exact value.
string qop = this.QualityOfProtections != null ? this.QualityOfProtections.TrimAndLower() : null;
if (qop == null)
{
string HA2 = string.Concat(request.MethodType.ToString().ToUpper(), ":", request.CurrentUri.GetRequestPathAndQueryURL()).CalculateMD5Hash();
response = string.Format("{0}:{1}:{2}", HA1, Nonce, HA2).CalculateMD5Hash();
}
else if (qop.Contains("auth-int"))
{
qop = "auth-int";
byte[] entityBody = request.GetEntityBody();
if (entityBody == null)
entityBody = string.Empty.GetASCIIBytes();
string HA2 = string.Format("{0}:{1}:{2}", request.MethodType.ToString().ToUpper(), request.CurrentUri.GetRequestPathAndQueryURL(), entityBody.CalculateMD5Hash()).CalculateMD5Hash();
response = string.Format("{0}:{1}:{2}:{3}:{4}:{5}", HA1, Nonce, ncvalue, cnonce, qop, HA2).CalculateMD5Hash();
}
else if (qop.Contains("auth"))
{
qop = "auth";
string HA2 = string.Concat(request.MethodType.ToString().ToUpper(), ":", request.CurrentUri.GetRequestPathAndQueryURL()).CalculateMD5Hash();
response = string.Format("{0}:{1}:{2}:{3}:{4}:{5}", HA1, Nonce, ncvalue, cnonce, qop, HA2).CalculateMD5Hash();
}
else //throw new NotSupportedException("Unrecognized Quality of Protection value found: " + this.QualityOfProtections);
return string.Empty;
string result = string.Format("Digest username=\"{0}\", realm=\"{1}\", nonce=\"{2}\", uri=\"{3}\", cnonce=\"{4}\", response=\"{5}\"",
credentials.UserName, Realm, Nonce, request.Uri.GetRequestPathAndQueryURL(), cnonce, response);
if (qop != null)
result += String.Concat(", qop=\"", qop, "\", nc=", ncvalue);
if (!string.IsNullOrEmpty(Opaque))
result = String.Concat(result, ", opaque=\"", Opaque, "\"");
return result;
}// end of case "digest":
default:
break;
}
}
catch
{
}
return string.Empty;
}
public bool IsUriProtected(Uri uri)
{
// http://tools.ietf.org/html/rfc2617#section-3.2.1
// An absoluteURI in this list may refer to
// a different server than the one being accessed. The client can use
// this list to determine the set of URIs for which the same
// authentication information may be sent: any URI that has a URI in
// this list as a prefix (after both have been made absolute) may be
// assumed to be in the same protection space. If this directive is
// omitted or its value is empty, the client should assume that the
// protection space consists of all URIs on the responding server.
if (string.CompareOrdinal(uri.Host, this.Uri.Host) != 0)
return false;
string uriStr = uri.ToString();
if (ProtectedUris != null && ProtectedUris.Count > 0)
for (int i = 0; i < ProtectedUris.Count; ++i)
if (uriStr.Contains(ProtectedUris[i]))
return true;
return true;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 03ce0d220c43c4dec924dc398fd42971
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
namespace BestHTTP.Authentication
{
/// <summary>
/// Stores and manages already received digest infos.
/// </summary>
internal static class DigestStore
{
private static Dictionary<string, Digest> Digests = new Dictionary<string, Digest>();
private static object Locker = new object();
/// <summary>
/// Array of algorithms that the plugin supports. It's in the order of priority(first has the highest priority).
/// </summary>
private static string[] SupportedAlgorithms = new string[] { "digest", "basic" };
internal static Digest Get(Uri uri)
{
lock (Locker)
{
Digest digest = null;
if (Digests.TryGetValue(uri.Host, out digest))
if (!digest.IsUriProtected(uri))
return null;
return digest;
}
}
/// <summary>
/// It will retrive or create a new Digest for the given Uri.
/// </summary>
/// <param name="uri"></param>
/// <returns></returns>
public static Digest GetOrCreate(Uri uri)
{
lock (Locker)
{
Digest digest = null;
if (!Digests.TryGetValue(uri.Host, out digest))
Digests.Add(uri.Host, digest = new Digest(uri));
return digest;
}
}
public static void Remove(Uri uri)
{
lock(Locker)
Digests.Remove(uri.Host);
}
public static string FindBest(List<string> authHeaders)
{
if (authHeaders == null || authHeaders.Count == 0)
return string.Empty;
List<string> headers = new List<string>(authHeaders.Count);
for (int i = 0; i < authHeaders.Count; ++i)
headers.Add(authHeaders[i].ToLower());
for (int i = 0; i < SupportedAlgorithms.Length; ++i)
{
int idx = headers.FindIndex((header) => header.StartsWith(SupportedAlgorithms[i]));
if (idx != -1)
return authHeaders[idx];
}
return string.Empty;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: aaeb6dbf1bcf54db7bceb9b72eb92745
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4fbf65d50c3c74fdd8149938611c3b06
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,400 @@
#if !BESTHTTP_DISABLE_CACHING && (!UNITY_WEBGL || UNITY_EDITOR)
using System;
using System.Collections.Generic;
#if NETFX_CORE
using FileStream = BestHTTP.PlatformSupport.IO.FileStream;
using Directory = BestHTTP.PlatformSupport.IO.Directory;
using File = BestHTTP.PlatformSupport.IO.File;
using BestHTTP.PlatformSupport.IO;
#else
using FileStream = System.IO.FileStream;
using System.IO;
#endif
namespace BestHTTP.Caching
{
using BestHTTP.Extensions;
/// <summary>
/// Holds all metadata that need for efficient caching, so we don't need to touch the disk to load headers.
/// </summary>
internal class HTTPCacheFileInfo : IComparable<HTTPCacheFileInfo>
{
#region Properties
/// <summary>
/// The uri that this HTTPCacheFileInfo belongs to.
/// </summary>
internal Uri Uri { get; set; }
/// <summary>
/// The last access time to this cache entity. The date is in UTC.
/// </summary>
internal DateTime LastAccess { get; set; }
/// <summary>
/// The length of the cache entity's body.
/// </summary>
internal int BodyLength { get; set; }
/// <summary>
/// ETag of the entity.
/// </summary>
private string ETag { get; set; }
/// <summary>
/// LastModified date of the entity.
/// </summary>
private string LastModified { get; set; }
/// <summary>
/// When the cache will expire.
/// </summary>
private DateTime Expires { get; set; }
/// <summary>
/// The age that came with the response
/// </summary>
private long Age { get; set; }
/// <summary>
/// Maximum how long the entry should served from the cache without revalidation.
/// </summary>
private long MaxAge { get; set; }
/// <summary>
/// The Date that came with the response.
/// </summary>
private DateTime Date { get; set; }
/// <summary>
/// Indicates whether the entity must be revalidated with the server or can be serverd directly from the cache without touching the server.
/// </summary>
private bool MustRevalidate { get; set; }
/// <summary>
/// The date and time when the HTTPResponse received.
/// </summary>
private DateTime Received { get; set; }
/// <summary>
/// Cached path.
/// </summary>
private string ConstructedPath { get; set; }
/// <summary>
/// This is the index of the enity. Filenames are generated from this value.
/// </summary>
internal UInt64 MappedNameIDX { get; set; }
#endregion
#region Constructors
internal HTTPCacheFileInfo(Uri uri)
:this(uri, DateTime.UtcNow, -1)
{
}
internal HTTPCacheFileInfo(Uri uri, DateTime lastAcces, int bodyLength)
{
this.Uri = uri;
this.LastAccess = lastAcces;
this.BodyLength = bodyLength;
this.MaxAge = -1;
this.MappedNameIDX = HTTPCacheService.GetNameIdx();
}
internal HTTPCacheFileInfo(Uri uri, System.IO.BinaryReader reader, int version)
{
this.Uri = uri;
this.LastAccess = DateTime.FromBinary(reader.ReadInt64());
this.BodyLength = reader.ReadInt32();
switch(version)
{
case 2:
this.MappedNameIDX = reader.ReadUInt64();
goto case 1;
case 1:
{
this.ETag = reader.ReadString();
this.LastModified = reader.ReadString();
this.Expires = DateTime.FromBinary(reader.ReadInt64());
this.Age = reader.ReadInt64();
this.MaxAge = reader.ReadInt64();
this.Date = DateTime.FromBinary(reader.ReadInt64());
this.MustRevalidate = reader.ReadBoolean();
this.Received = DateTime.FromBinary(reader.ReadInt64());
break;
}
}
}
#endregion
#region Helper Functions
internal void SaveTo(System.IO.BinaryWriter writer)
{
writer.Write(LastAccess.ToBinary());
writer.Write(BodyLength);
writer.Write(MappedNameIDX);
writer.Write(ETag);
writer.Write(LastModified);
writer.Write(Expires.ToBinary());
writer.Write(Age);
writer.Write(MaxAge);
writer.Write(Date.ToBinary());
writer.Write(MustRevalidate);
writer.Write(Received.ToBinary());
}
private string GetPath()
{
if (ConstructedPath != null)
return ConstructedPath;
return ConstructedPath = System.IO.Path.Combine(HTTPCacheService.CacheFolder, MappedNameIDX.ToString("X"));
}
internal bool IsExists()
{
if (!HTTPCacheService.IsSupported)
return false;
return File.Exists(GetPath());
}
internal void Delete()
{
if (!HTTPCacheService.IsSupported)
return;
string path = GetPath();
try
{
File.Delete(path);
}
catch
{ }
finally
{
Reset();
}
}
private void Reset()
{
// MappedNameIDX will remain the same. When we re-save an entity, it will not reset the MappedNameIDX.
this.BodyLength = -1;
this.ETag = string.Empty;
this.Expires = DateTime.FromBinary(0);
this.LastModified = string.Empty;
this.Age = 0;
this.MaxAge = -1;
this.Date = DateTime.FromBinary(0);
this.MustRevalidate = false;
this.Received = DateTime.FromBinary(0);
}
#endregion
#region Caching
private void SetUpCachingValues(HTTPResponse response)
{
this.ETag = response.GetFirstHeaderValue("ETag").ToStrOrEmpty();
this.Expires = response.GetFirstHeaderValue("Expires").ToDateTime(DateTime.FromBinary(0));
this.LastModified = response.GetFirstHeaderValue("Last-Modified").ToStrOrEmpty();
this.Age = response.GetFirstHeaderValue("Age").ToInt64(0);
this.Date = response.GetFirstHeaderValue("Date").ToDateTime(DateTime.FromBinary(0));
string cacheControl = response.GetFirstHeaderValue("cache-control");
if (!string.IsNullOrEmpty(cacheControl))
{
string[] kvp = cacheControl.FindOption("max-age");
if (kvp != null)
{
// Some cache proxies will return float values
double maxAge;
if (double.TryParse(kvp[1], out maxAge))
this.MaxAge = (int)maxAge;
}
this.MustRevalidate = cacheControl.ToLower().Contains("must-revalidate");
}
this.Received = DateTime.UtcNow;
}
internal bool WillExpireInTheFuture()
{
if (!IsExists())
return false;
if (MustRevalidate)
return false;
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.2.4 :
// The max-age directive takes priority over Expires
if (MaxAge != -1)
{
// Age calculation:
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.2.3
long apparent_age = Math.Max(0, (long)(Received - Date).TotalSeconds);
long corrected_received_age = Math.Max(apparent_age, Age);
long resident_time = (long)(DateTime.UtcNow - Date).TotalSeconds;
long current_age = corrected_received_age + resident_time;
return current_age < MaxAge;
}
return Expires > DateTime.UtcNow;
}
internal void SetUpRevalidationHeaders(HTTPRequest request)
{
if (!IsExists())
return;
// -If an entity tag has been provided by the origin server, MUST use that entity tag in any cache-conditional request (using If-Match or If-None-Match).
// -If only a Last-Modified value has been provided by the origin server, SHOULD use that value in non-subrange cache-conditional requests (using If-Modified-Since).
// -If both an entity tag and a Last-Modified value have been provided by the origin server, SHOULD use both validators in cache-conditional requests. This allows both HTTP/1.0 and HTTP/1.1 caches to respond appropriately.
if (!string.IsNullOrEmpty(ETag))
request.AddHeader("If-None-Match", ETag);
if (!string.IsNullOrEmpty(LastModified))
request.AddHeader("If-Modified-Since", LastModified);
}
internal System.IO.Stream GetBodyStream(out int length)
{
if (!IsExists())
{
length = 0;
return null;
}
length = BodyLength;
LastAccess = DateTime.UtcNow;
FileStream stream = new FileStream(GetPath(), FileMode.Open, FileAccess.Read, FileShare.Read);
stream.Seek(-length, System.IO.SeekOrigin.End);
return stream;
}
internal HTTPResponse ReadResponseTo(HTTPRequest request)
{
if (!IsExists())
return null;
LastAccess = DateTime.UtcNow;
using (FileStream stream = new FileStream(GetPath(), FileMode.Open, FileAccess.Read, FileShare.Read))
{
var response = new HTTPResponse(request, stream, request.UseStreaming, true);
response.Receive(BodyLength);
return response;
}
}
internal void Store(HTTPResponse response)
{
if (!HTTPCacheService.IsSupported)
return;
string path = GetPath();
// Path name too long, we don't want to get exceptions
if (path.Length > HTTPManager.MaxPathLength)
return;
if (File.Exists(path))
Delete();
using (FileStream writer = new FileStream(path, FileMode.Create))
{
writer.WriteLine("HTTP/1.1 {0} {1}", response.StatusCode, response.Message);
foreach (var kvp in response.Headers)
{
for (int i = 0; i < kvp.Value.Count; ++i)
writer.WriteLine("{0}: {1}", kvp.Key, kvp.Value[i]);
}
writer.WriteLine();
writer.Write(response.Data, 0, response.Data.Length);
}
BodyLength = response.Data.Length;
LastAccess = DateTime.UtcNow;
SetUpCachingValues(response);
}
internal System.IO.Stream GetSaveStream(HTTPResponse response)
{
if (!HTTPCacheService.IsSupported)
return null;
LastAccess = DateTime.UtcNow;
string path = GetPath();
if (File.Exists(path))
Delete();
// Path name too long, we don't want to get exceptions
if (path.Length > HTTPManager.MaxPathLength)
return null;
// First write out the headers
using (FileStream writer = new FileStream(path, FileMode.Create))
{
writer.WriteLine("HTTP/1.1 {0} {1}", response.StatusCode, response.Message);
foreach (var kvp in response.Headers)
{
for (int i = 0; i < kvp.Value.Count; ++i)
writer.WriteLine("{0}: {1}", kvp.Key, kvp.Value[i]);
}
writer.WriteLine();
}
// If caching is enabled and the response is from cache, and no content-length header set, then we set one to the response.
if (response.IsFromCache && !response.Headers.ContainsKey("content-length"))
response.Headers.Add("content-length", new List<string> { BodyLength.ToString() });
SetUpCachingValues(response);
// then create the stream with Append FileMode
return new FileStream(GetPath(), FileMode.Append);
}
#endregion
#region IComparable<HTTPCacheFileInfo>
public int CompareTo(HTTPCacheFileInfo other)
{
return this.LastAccess.CompareTo(other.LastAccess);
}
#endregion
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0d1a8ac8bd23145b2b3b631adfdd3984
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,44 @@
#if !BESTHTTP_DISABLE_CACHING && (!UNITY_WEBGL || UNITY_EDITOR)
using System;
using System.Collections.Generic;
namespace BestHTTP.Caching
{
sealed class HTTPCacheFileLock
{
private static Dictionary<Uri, object> FileLocks = new Dictionary<Uri, object>();
private static object SyncRoot = new object();
internal static object Acquire(Uri uri)
{
lock (SyncRoot)
{
object fileLock;
if (!FileLocks.TryGetValue(uri, out fileLock))
FileLocks.Add(uri, fileLock = new object());
return fileLock;
}
}
internal static void Remove(Uri uri)
{
lock (SyncRoot)
{
if (FileLocks.ContainsKey(uri))
FileLocks.Remove(uri);
}
}
internal static void Clear()
{
lock (SyncRoot)
{
FileLocks.Clear();
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c34f20a1b4984115bcfc30850e04235
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,27 @@
#if !BESTHTTP_DISABLE_CACHING && (!UNITY_WEBGL || UNITY_EDITOR)
using System;
namespace BestHTTP.Caching
{
public sealed class HTTPCacheMaintananceParams
{
/// <summary>
/// Delete cache entries that accessed older then this value. If TimeSpan.FromSeconds(0) is used then all cache entries will be deleted. With TimeSpan.FromDays(2) entries that older then two days will be deleted.
/// </summary>
public TimeSpan DeleteOlder { get; private set; }
/// <summary>
/// If the cache is larger then the MaxCacheSize after the first maintanance step, then the maintanance job will forcedelete cache entries starting with the oldest last accessed one.
/// </summary>
public ulong MaxCacheSize { get; private set; }
public HTTPCacheMaintananceParams(TimeSpan deleteOlder, ulong maxCacheSize)
{
this.DeleteOlder = deleteOlder;
this.MaxCacheSize = maxCacheSize;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0647cf5086003446395aff0aa08615c5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,728 @@
#if !BESTHTTP_DISABLE_CACHING && (!UNITY_WEBGL || UNITY_EDITOR)
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
#if NETFX_CORE
using FileStream = BestHTTP.PlatformSupport.IO.FileStream;
using Directory = BestHTTP.PlatformSupport.IO.Directory;
using File = BestHTTP.PlatformSupport.IO.File;
using BestHTTP.PlatformSupport.IO;
//Disable CD4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
#pragma warning disable 4014
#else
using FileStream = System.IO.FileStream;
using Directory = System.IO.Directory;
using System.IO;
#endif
//
// Version 1: Initial release
// Version 2: Filenames are generated from an index.
//
namespace BestHTTP.Caching
{
using BestHTTP.Extensions;
public sealed class UriComparer : IEqualityComparer<Uri>
{
public bool Equals(Uri x, Uri y)
{
return Uri.Compare(x, y, UriComponents.HttpRequestUrl, UriFormat.SafeUnescaped, StringComparison.Ordinal) == 0;
}
public int GetHashCode(Uri uri)
{
return uri.ToString().GetHashCode();
}
}
public static class HTTPCacheService
{
#region Properties & Fields
/// <summary>
/// Library file-format versioning support
/// </summary>
private const int LibraryVersion = 2;
public static bool IsSupported
{
get
{
if (IsSupportCheckDone)
return isSupported;
try
{
File.Exists(HTTPManager.GetRootCacheFolder());
isSupported = true;
}
catch
{
isSupported = false;
HTTPManager.Logger.Warning("HTTPCacheService", "Cache Service Disabled!");
}
finally
{
IsSupportCheckDone = true;
}
return isSupported;
}
}
private static bool isSupported;
private static bool IsSupportCheckDone;
private static Dictionary<Uri, HTTPCacheFileInfo> library;
private static Dictionary<Uri, HTTPCacheFileInfo> Library { get { LoadLibrary(); return library; } }
private static Dictionary<UInt64, HTTPCacheFileInfo> UsedIndexes = new Dictionary<ulong, HTTPCacheFileInfo>();
internal static string CacheFolder { get; private set; }
private static string LibraryPath { get; set; }
private static bool InClearThread;
private static bool InMaintainenceThread;
/// <summary>
/// Stores the index of the next stored entity. The entity's file name is generated from this index.
/// </summary>
private static UInt64 NextNameIDX;
#endregion
static HTTPCacheService()
{
NextNameIDX = 0x0001;
}
#region Common Functions
internal static void CheckSetup()
{
if (!HTTPCacheService.IsSupported)
return;
try
{
SetupCacheFolder();
LoadLibrary();
}
catch
{ }
}
internal static void SetupCacheFolder()
{
if (!HTTPCacheService.IsSupported)
return;
try
{
if (string.IsNullOrEmpty(CacheFolder) || string.IsNullOrEmpty(LibraryPath))
{
CacheFolder = System.IO.Path.Combine(HTTPManager.GetRootCacheFolder(), "HTTPCache");
if (!Directory.Exists(CacheFolder))
Directory.CreateDirectory(CacheFolder);
LibraryPath = System.IO.Path.Combine(HTTPManager.GetRootCacheFolder(), "Library");
}
}
catch
{
isSupported = false;
HTTPManager.Logger.Warning("HTTPCacheService", "Cache Service Disabled!");
}
}
internal static UInt64 GetNameIdx()
{
lock(Library)
{
UInt64 result = NextNameIDX;
do
{
NextNameIDX = ++NextNameIDX % UInt64.MaxValue;
} while (UsedIndexes.ContainsKey(NextNameIDX));
return result;
}
}
internal static bool HasEntity(Uri uri)
{
if (!IsSupported)
return false;
lock (Library)
return Library.ContainsKey(uri);
}
internal static bool DeleteEntity(Uri uri, bool removeFromLibrary = true)
{
if (!IsSupported)
return false;
object uriLocker = HTTPCacheFileLock.Acquire(uri);
// Just use lock now: http://forum.unity3d.com/threads/4-6-ios-64-bit-beta.290551/page-6#post-1937033
// To avoid a dead-lock we try acquire the lock on this uri only for a little time.
// If we can't acquire it, its better to just return without risking a deadlock.
//if (Monitor.TryEnter(uriLocker, TimeSpan.FromSeconds(0.5f)))
lock(uriLocker)
{
try
{
lock (Library)
{
HTTPCacheFileInfo info;
bool inStats = Library.TryGetValue(uri, out info);
if (inStats)
info.Delete();
if (inStats && removeFromLibrary)
{
Library.Remove(uri);
UsedIndexes.Remove(info.MappedNameIDX);
}
return true;
}
}
finally
{
//Monitor.Exit(uriLocker);
}
}
//return false;
}
internal static bool IsCachedEntityExpiresInTheFuture(HTTPRequest request)
{
if (!IsSupported)
return false;
HTTPCacheFileInfo info;
lock (Library)
if (Library.TryGetValue(request.CurrentUri, out info))
return info.WillExpireInTheFuture();
return false;
}
/// <summary>
/// Utility function to set the cache control headers according to the spec.: http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.4
/// </summary>
/// <param name="request"></param>
internal static void SetHeaders(HTTPRequest request)
{
if (!IsSupported)
return;
HTTPCacheFileInfo info;
lock (Library)
if (Library.TryGetValue(request.CurrentUri, out info))
info.SetUpRevalidationHeaders(request);
}
#endregion
#region Get Functions
internal static System.IO.Stream GetBody(Uri uri, out int length)
{
length = 0;
if (!IsSupported)
return null;
HTTPCacheFileInfo info;
lock (Library)
if (Library.TryGetValue(uri, out info))
return info.GetBodyStream(out length);
return null;
}
internal static HTTPResponse GetFullResponse(HTTPRequest request)
{
if (!IsSupported)
return null;
HTTPCacheFileInfo info;
lock (Library)
if (Library.TryGetValue(request.CurrentUri, out info))
return info.ReadResponseTo(request);
return null;
}
#endregion
#region Storing
/// <summary>
/// Checks if the given response can be cached. http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4
/// </summary>
/// <returns>Returns true if cacheable, false otherwise.</returns>
internal static bool IsCacheble(Uri uri, HTTPMethods method, HTTPResponse response)
{
if (!IsSupported)
return false;
if (method != HTTPMethods.Get)
return false;
if (response == null)
return false;
// https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.12 - Cache Replacement
// It MAY insert it into cache storage and MAY, if it meets all other requirements, use it to respond to any future requests that would previously have caused the old response to be returned.
//if (response.StatusCode == 304)
// return false;
if (response.StatusCode < 200 || response.StatusCode >= 400)
return false;
//http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.2
var cacheControls = response.GetHeaderValues("cache-control");
if (cacheControls != null)
{
if (cacheControls.Exists(headerValue => {
string value = headerValue.ToLower();
return value.Contains("no-store") || value.Contains("no-cache");
}))
return false;
}
var pragmas = response.GetHeaderValues("pragma");
if (pragmas != null)
{
if (pragmas.Exists(headerValue => {
string value = headerValue.ToLower();
return value.Contains("no-store") || value.Contains("no-cache");
}))
return false;
}
// Responses with byte ranges not supported yet.
var byteRanges = response.GetHeaderValues("content-range");
if (byteRanges != null)
return false;
return true;
}
internal static HTTPCacheFileInfo Store(Uri uri, HTTPMethods method, HTTPResponse response)
{
if (response == null || response.Data == null || response.Data.Length == 0)
return null;
if (!IsSupported)
return null;
HTTPCacheFileInfo info = null;
lock (Library)
{
if (!Library.TryGetValue(uri, out info))
{
Library.Add(uri, info = new HTTPCacheFileInfo(uri));
UsedIndexes.Add(info.MappedNameIDX, info);
}
try
{
info.Store(response);
if (HTTPManager.Logger.Level == Logger.Loglevels.All)
HTTPManager.Logger.Verbose("HTTPCacheService", string.Format("{0} - Saved to cache", uri.ToString()));
}
catch
{
// If something happens while we write out the response, than we will delete it because it might be in an invalid state.
DeleteEntity(uri);
throw;
}
}
return info;
}
internal static System.IO.Stream PrepareStreamed(Uri uri, HTTPResponse response)
{
if (!IsSupported)
return null;
HTTPCacheFileInfo info;
lock (Library)
{
if (!Library.TryGetValue(uri, out info))
{
Library.Add(uri, info = new HTTPCacheFileInfo(uri));
UsedIndexes.Add(info.MappedNameIDX, info);
}
try
{
return info.GetSaveStream(response);
}
catch
{
// If something happens while we write out the response, than we will delete it because it might be in an invalid state.
DeleteEntity(uri);
throw;
}
}
}
#endregion
#region Public Maintenance Functions
/// <summary>
/// Deletes all cache entity. Non blocking.
/// <remarks>Call it only if there no requests currently processed, because cache entries can be deleted while a server sends back a 304 result, so there will be no data to read from the cache!</remarks>
/// </summary>
public static void BeginClear()
{
if (!IsSupported)
return;
if (InClearThread)
return;
InClearThread = true;
SetupCacheFolder();
#if !NETFX_CORE
ThreadPool.QueueUserWorkItem(new WaitCallback((param) => ClearImpl(param)));
//new Thread(ClearImpl).Start();
#else
Windows.System.Threading.ThreadPool.RunAsync(ClearImpl);
#endif
}
private static void ClearImpl(object param)
{
if (!IsSupported)
return;
try
{
// GetFiles will return a string array that contains the files in the folder with the full path
string[] cacheEntries = Directory.GetFiles(CacheFolder);
for (int i = 0; i < cacheEntries.Length; ++i)
{
// We need a try-catch block because between the Directory.GetFiles call and the File.Delete calls a maintenance job, or other file operations can delete any file from the cache folder.
// So while there might be some problem with any file, we don't want to abort the whole for loop
try
{
File.Delete(cacheEntries[i]);
}
catch
{ }
}
}
finally
{
UsedIndexes.Clear();
library.Clear();
NextNameIDX = 0x0001;
SaveLibrary();
InClearThread = false;
}
}
/// <summary>
/// Deletes all expired cache entity.
/// <remarks>Call it only if there no requests currently processed, because cache entries can be deleted while a server sends back a 304 result, so there will be no data to read from the cache!</remarks>
/// </summary>
public static void BeginMaintainence(HTTPCacheMaintananceParams maintananceParam)
{
if (maintananceParam == null)
throw new ArgumentNullException("maintananceParams == null");
if (!HTTPCacheService.IsSupported)
return;
if (InMaintainenceThread)
return;
InMaintainenceThread = true;
SetupCacheFolder();
#if !NETFX_CORE
ThreadPool.QueueUserWorkItem(new WaitCallback((param) =>
//new Thread((param) =>
#else
Windows.System.Threading.ThreadPool.RunAsync((param) =>
#endif
{
try
{
lock (Library)
{
// Delete cache entries older than the given time.
DateTime deleteOlderAccessed = DateTime.UtcNow - maintananceParam.DeleteOlder;
List<HTTPCacheFileInfo> removedEntities = new List<HTTPCacheFileInfo>();
foreach (var kvp in Library)
if (kvp.Value.LastAccess < deleteOlderAccessed)
{
if (DeleteEntity(kvp.Key, false))
removedEntities.Add(kvp.Value);
}
for (int i = 0; i < removedEntities.Count; ++i)
{
Library.Remove(removedEntities[i].Uri);
UsedIndexes.Remove(removedEntities[i].MappedNameIDX);
}
removedEntities.Clear();
ulong cacheSize = GetCacheSize();
// This step will delete all entries starting with the oldest LastAccess property while the cache size greater then the MaxCacheSize in the given param.
if (cacheSize > maintananceParam.MaxCacheSize)
{
List<HTTPCacheFileInfo> fileInfos = new List<HTTPCacheFileInfo>(library.Count);
foreach(var kvp in library)
fileInfos.Add(kvp.Value);
fileInfos.Sort();
int idx = 0;
while (cacheSize >= maintananceParam.MaxCacheSize && idx < fileInfos.Count)
{
try
{
var fi = fileInfos[idx];
ulong length = (ulong)fi.BodyLength;
DeleteEntity(fi.Uri);
cacheSize -= length;
}
catch
{}
finally
{
++idx;
}
}
}
}
}
finally
{
SaveLibrary();
InMaintainenceThread = false;
}
}
#if !NETFX_CORE
));
#else
);
#endif
}
public static int GetCacheEntityCount()
{
if (!HTTPCacheService.IsSupported)
return 0;
CheckSetup();
lock(Library)
return Library.Count;
}
public static ulong GetCacheSize()
{
ulong size = 0;
if (!IsSupported)
return size;
CheckSetup();
lock (Library)
foreach (var kvp in Library)
if (kvp.Value.BodyLength > 0)
size += (ulong)kvp.Value.BodyLength;
return size;
}
#endregion
#region Cache Library Management
private static void LoadLibrary()
{
// Already loaded?
if (library != null)
return;
if (!IsSupported)
return;
library = new Dictionary<Uri, HTTPCacheFileInfo>(new UriComparer());
if (!File.Exists(LibraryPath))
{
DeleteUnusedFiles();
return;
}
try
{
int version;
lock (library)
{
using (var fs = new FileStream(LibraryPath, FileMode.Open))
using (var br = new System.IO.BinaryReader(fs))
{
version = br.ReadInt32();
if (version > 1)
NextNameIDX = br.ReadUInt64();
int statCount = br.ReadInt32();
for (int i = 0; i < statCount; ++i)
{
Uri uri = new Uri(br.ReadString());
var entity = new HTTPCacheFileInfo(uri, br, version);
if (entity.IsExists())
{
library.Add(uri, entity);
if (version > 1)
UsedIndexes.Add(entity.MappedNameIDX, entity);
}
}
}
}
if (version == 1)
BeginClear();
else
DeleteUnusedFiles();
}
catch
{}
}
internal static void SaveLibrary()
{
if (library == null)
return;
if (!IsSupported)
return;
try
{
lock (Library)
{
using (var fs = new FileStream(LibraryPath, FileMode.Create))
using (var bw = new System.IO.BinaryWriter(fs))
{
bw.Write(LibraryVersion);
bw.Write(NextNameIDX);
bw.Write(Library.Count);
foreach (var kvp in Library)
{
bw.Write(kvp.Key.ToString());
kvp.Value.SaveTo(bw);
}
}
}
}
catch
{}
}
internal static void SetBodyLength(Uri uri, int bodyLength)
{
if (!IsSupported)
return;
lock (Library)
{
HTTPCacheFileInfo fileInfo;
if (Library.TryGetValue(uri, out fileInfo))
fileInfo.BodyLength = bodyLength;
else
{
Library.Add(uri, fileInfo = new HTTPCacheFileInfo(uri, DateTime.UtcNow, bodyLength));
UsedIndexes.Add(fileInfo.MappedNameIDX, fileInfo);
}
}
}
/// <summary>
/// Deletes all files from the cache folder that isn't in the Library.
/// </summary>
private static void DeleteUnusedFiles()
{
if (!IsSupported)
return;
CheckSetup();
// GetFiles will return a string array that contains the files in the folder with the full path
string[] cacheEntries = Directory.GetFiles(CacheFolder);
for (int i = 0; i < cacheEntries.Length; ++i)
{
// We need a try-catch block because between the Directory.GetFiles call and the File.Delete calls a maintenance job, or other file operations can delete any file from the cache folder.
// So while there might be some problem with any file, we don't want to abort the whole for loop
try
{
string filename = System.IO.Path.GetFileName(cacheEntries[i]);
UInt64 idx = 0;
bool deleteFile = false;
if (UInt64.TryParse(filename, System.Globalization.NumberStyles.AllowHexSpecifier, null, out idx))
lock (Library)
deleteFile = !UsedIndexes.ContainsKey(idx);
else
deleteFile = true;
if (deleteFile)
File.Delete(cacheEntries[i]);
}
catch
{}
}
}
#endregion
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 07603236d23e341e5957eebda17f43a6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fbb54077808034d35aeaa540db8ce5b1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,218 @@
using System;
using System.Threading;
#if NETFX_CORE
using System.Threading.Tasks;
//Disable CD4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
#pragma warning disable 4014
//Disable warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
#pragma warning disable 1998
#endif
namespace BestHTTP
{
internal delegate void HTTPConnectionRecycledDelegate(ConnectionBase conn);
internal abstract class ConnectionBase : IDisposable
{
#region Public Properties
/// <summary>
/// The address of the server that this connection is bound to.
/// </summary>
public string ServerAddress { get; protected set; }
/// <summary>
/// The state of this connection.
/// </summary>
public HTTPConnectionStates State { get; protected set; }
/// <summary>
/// It's true if this connection is available to process a HTTPRequest.
/// </summary>
public bool IsFree { get { return State == HTTPConnectionStates.Initial || State == HTTPConnectionStates.Free; } }
/// <summary>
/// Returns true if it's an active connection.
/// </summary>
public bool IsActive { get { return State > HTTPConnectionStates.Initial && State < HTTPConnectionStates.Free; } }
/// <summary>
/// If the State is HTTPConnectionStates.Processing, then it holds a HTTPRequest instance. Otherwise it's null.
/// </summary>
public HTTPRequest CurrentRequest { get; protected set; }
public virtual bool IsRemovable { get { return IsFree && (DateTime.UtcNow - LastProcessTime) > HTTPManager.MaxConnectionIdleTime; } }
/// <summary>
/// When we start to process the current request. It's set after the connection is established.
/// </summary>
public DateTime StartTime { get; protected set; }
/// <summary>
/// When this connection timed out.
/// </summary>
public DateTime TimedOutStart { get; protected set; }
#if !BESTHTTP_DISABLE_PROXY
protected HTTPProxy Proxy { get; set; }
public bool HasProxy { get { return Proxy != null; } }
#endif
public Uri LastProcessedUri { get; protected set; }
#endregion
#region Protected Fields
protected DateTime LastProcessTime;
protected HTTPConnectionRecycledDelegate OnConnectionRecycled = null;
#endregion
#region Privates
private bool IsThreaded;
#endregion
public ConnectionBase(string serverAddress)
:this(serverAddress, true)
{}
public ConnectionBase(string serverAddress, bool threaded)
{
this.ServerAddress = serverAddress;
this.State = HTTPConnectionStates.Initial;
this.LastProcessTime = DateTime.UtcNow;
this.IsThreaded = threaded;
}
internal abstract void Abort(HTTPConnectionStates hTTPConnectionStates);
internal void Process(HTTPRequest request)
{
if (State == HTTPConnectionStates.Processing)
throw new Exception("Connection already processing a request!");
StartTime = DateTime.MaxValue;
State = HTTPConnectionStates.Processing;
CurrentRequest = request;
if (IsThreaded)
{
#if NETFX_CORE
Windows.System.Threading.ThreadPool.RunAsync(ThreadFunc);
#else
ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadFunc));
//new Thread(ThreadFunc)
// .Start();
#endif
}
else
ThreadFunc(null);
}
protected virtual
#if NETFX_CORE
async
#endif
void ThreadFunc(object param)
{
}
internal void HandleProgressCallback()
{
if (CurrentRequest.OnProgress != null && CurrentRequest.DownloadProgressChanged)
{
try
{
CurrentRequest.OnProgress(CurrentRequest, CurrentRequest.Downloaded, CurrentRequest.DownloadLength);
}
catch (Exception ex)
{
HTTPManager.Logger.Exception("ConnectionBase", "HandleProgressCallback - OnProgress", ex);
}
CurrentRequest.DownloadProgressChanged = false;
}
if (CurrentRequest.OnUploadProgress != null && CurrentRequest.UploadProgressChanged)
{
try
{
CurrentRequest.OnUploadProgress(CurrentRequest, CurrentRequest.Uploaded, CurrentRequest.UploadLength);
}
catch (Exception ex)
{
HTTPManager.Logger.Exception("ConnectionBase", "HandleProgressCallback - OnUploadProgress", ex);
}
CurrentRequest.UploadProgressChanged = false;
}
}
internal void HandleCallback()
{
try
{
HandleProgressCallback();
if (State == HTTPConnectionStates.Upgraded)
{
if (CurrentRequest != null && CurrentRequest.Response != null && CurrentRequest.Response.IsUpgraded)
CurrentRequest.UpgradeCallback();
State = HTTPConnectionStates.WaitForProtocolShutdown;
}
else
CurrentRequest.CallCallback();
}
catch (Exception ex)
{
HTTPManager.Logger.Exception("ConnectionBase", "HandleCallback", ex);
}
}
internal void Recycle(HTTPConnectionRecycledDelegate onConnectionRecycled)
{
OnConnectionRecycled = onConnectionRecycled;
if (!(State > HTTPConnectionStates.Initial && State < HTTPConnectionStates.WaitForProtocolShutdown) || State == HTTPConnectionStates.Redirected)
RecycleNow();
}
protected void RecycleNow()
{
if (State == HTTPConnectionStates.TimedOut ||
State == HTTPConnectionStates.Closed)
LastProcessTime = DateTime.MinValue;
State = HTTPConnectionStates.Free;
CurrentRequest = null;
if (OnConnectionRecycled != null)
{
OnConnectionRecycled(this);
OnConnectionRecycled = null;
}
}
#region Dispose Pattern
protected bool IsDisposed { get; private set; }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
IsDisposed = true;
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 19b89653c6a154828965eb801bae64c7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,311 @@
using System;
using System.Collections.Generic;
#if NETFX_CORE
using FileStream = BestHTTP.PlatformSupport.IO.FileStream;
using FileMode = BestHTTP.PlatformSupport.IO.FileMode;
using FileAccess = BestHTTP.PlatformSupport.IO.FileAccess;
using Directory = BestHTTP.PlatformSupport.IO.Directory;
using File = BestHTTP.PlatformSupport.IO.File;
#else
using FileStream = System.IO.FileStream;
using FileMode = System.IO.FileMode;
using FileAccess = System.IO.FileAccess;
#endif
using BestHTTP.Extensions;
namespace BestHTTP
{
sealed class StreamList : System.IO.Stream
{
private System.IO.Stream[] Streams;
private int CurrentIdx;
public StreamList(params System.IO.Stream[] streams)
{
this.Streams = streams;
this.CurrentIdx = 0;
}
public override bool CanRead
{
get {
if (CurrentIdx >= Streams.Length)
return false;
return Streams[CurrentIdx].CanRead;
}
}
public override bool CanSeek { get { return false; } }
public override bool CanWrite
{
get {
if (CurrentIdx >= Streams.Length)
return false;
return Streams[CurrentIdx].CanWrite;
}
}
public override void Flush()
{
if (CurrentIdx >= Streams.Length)
return;
// We have to call the flush to all previous streams, as we may advanced the CurrentIdx
for (int i = 0; i <= CurrentIdx; ++i)
Streams[i].Flush();
}
public override long Length
{
get {
if (CurrentIdx >= Streams.Length)
return 0;
long length = 0;
for (int i = 0; i < Streams.Length; ++i)
length += Streams[i].Length;
return length;
}
}
public override int Read(byte[] buffer, int offset, int count)
{
if (CurrentIdx >= Streams.Length)
return -1;
int readCount = Streams[CurrentIdx].Read(buffer, offset, count);
while (readCount < count && CurrentIdx++ < Streams.Length)
{
readCount += Streams[CurrentIdx].Read(buffer, offset + readCount, count - readCount);
}
return readCount;
}
public override void Write(byte[] buffer, int offset, int count)
{
if (CurrentIdx >= Streams.Length)
return;
Streams[CurrentIdx].Write(buffer, offset, count);
}
public void Write(string str)
{
byte[] bytes = str.GetASCIIBytes();
this.Write(bytes, 0, bytes.Length);
}
protected override void Dispose(bool disposing)
{
for (int i = 0; i < Streams.Length; ++i)
{
try
{
Streams[i].Dispose();
}
catch(Exception ex)
{
HTTPManager.Logger.Exception("StreamList", "Dispose", ex);
}
}
}
public override long Position
{
get
{
throw new NotImplementedException("Position get");
}
set
{
throw new NotImplementedException("Position set");
}
}
public override long Seek(long offset, System.IO.SeekOrigin origin)
{
if (CurrentIdx >= Streams.Length)
return 0;
return Streams[CurrentIdx].Seek(offset, origin);
}
public override void SetLength(long value)
{
throw new NotImplementedException("SetLength");
}
}
/*public static class AndroidFileHelper
{
// AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
// AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
public static Stream GetAPKFileStream(string path)
{
UnityEngine.AndroidJavaClass up = new UnityEngine.AndroidJavaClass("com.unity3d.player.UnityPlayer");
UnityEngine.AndroidJavaObject cActivity = up.GetStatic<UnityEngine.AndroidJavaObject>("currentActivity");
UnityEngine.AndroidJavaObject assetManager = cActivity.GetStatic<UnityEngine.AndroidJavaObject>("getAssets");
return new AndroidInputStream(assetManager.Call<UnityEngine.AndroidJavaObject>("open", path));
}
}
public sealed class AndroidInputStream : Stream
{
private UnityEngine.AndroidJavaObject baseStream;
public override bool CanRead
{
get { throw new NotImplementedException(); }
}
public override bool CanSeek
{
get { throw new NotImplementedException(); }
}
public override bool CanWrite
{
get { throw new NotImplementedException(); }
}
public override void Flush()
{
throw new NotImplementedException();
}
public override long Length
{
get { throw new NotImplementedException(); }
}
public override long Position
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public AndroidInputStream(UnityEngine.AndroidJavaObject inputStream)
{
this.baseStream = inputStream;
}
public override int Read(byte[] buffer, int offset, int count)
{
return this.baseStream.Call<int>("read", buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
}*/
internal sealed class FileConnection : ConnectionBase
{
public FileConnection(string serverAddress)
:base(serverAddress)
{ }
internal override void Abort(HTTPConnectionStates newState)
{
State = newState;
switch (State)
{
case HTTPConnectionStates.TimedOut: TimedOutStart = DateTime.UtcNow; break;
}
throw new NotImplementedException();
}
protected override void ThreadFunc(object param)
{
try
{
// Step 1 : create a stream with header information
// Step 2 : create a stream from the file
// Step 3 : create a StreamList
// Step 4 : create a HTTPResponse object
// Step 5 : call the Receive function of the response object
using (FileStream fs = new FileStream(this.CurrentRequest.CurrentUri.LocalPath, FileMode.Open, FileAccess.Read))
//using (Stream fs = AndroidFileHelper.GetAPKFileStream(this.CurrentRequest.CurrentUri.LocalPath))
using (StreamList stream = new StreamList(new System.IO.MemoryStream(), fs))
{
// This will write to the MemoryStream
stream.Write("HTTP/1.1 200 Ok\r\n");
stream.Write("Content-Type: application/octet-stream\r\n");
stream.Write("Content-Length: " + fs.Length.ToString() + "\r\n");
stream.Write("\r\n");
stream.Seek(0, System.IO.SeekOrigin.Begin);
base.CurrentRequest.Response = new HTTPResponse(base.CurrentRequest, stream, base.CurrentRequest.UseStreaming, false);
if (!CurrentRequest.Response.Receive())
CurrentRequest.Response = null;
}
}
catch(Exception ex)
{
if (CurrentRequest != null)
{
// Something gone bad, Response must be null!
CurrentRequest.Response = null;
switch (State)
{
case HTTPConnectionStates.AbortRequested:
CurrentRequest.State = HTTPRequestStates.Aborted;
break;
case HTTPConnectionStates.TimedOut:
CurrentRequest.State = HTTPRequestStates.TimedOut;
break;
default:
CurrentRequest.Exception = ex;
CurrentRequest.State = HTTPRequestStates.Error;
break;
}
}
}
finally
{
State = HTTPConnectionStates.Closed;
if (CurrentRequest.State == HTTPRequestStates.Processing)
{
if (CurrentRequest.Response != null)
CurrentRequest.State = HTTPRequestStates.Finished;
else
CurrentRequest.State = HTTPRequestStates.Error;
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2da61498eefa84751bf83deb77fc1754
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,395 @@
#if UNITY_WEBGL && !UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using BestHTTP.Authentication;
namespace BestHTTP
{
delegate void OnWebGLRequestHandlerDelegate(int nativeId, int httpStatus, IntPtr pBuffer, int length, int zero);
delegate void OnWebGLBufferDelegate(int nativeId, IntPtr pBuffer, int length);
delegate void OnWebGLProgressDelegate(int nativeId, int downloaded, int total);
delegate void OnWebGLErrorDelegate(int nativeId, string error);
delegate void OnWebGLTimeoutDelegate(int nativeId);
delegate void OnWebGLAbortedDelegate(int nativeId);
internal sealed class WebGLConnection : ConnectionBase
{
static Dictionary<int, WebGLConnection> Connections = new Dictionary<int, WebGLConnection>(4);
int NativeId;
MemoryStream Stream;
public WebGLConnection(string serverAddress)
: base(serverAddress, false)
{
XHR_SetLoglevel((byte)HTTPManager.Logger.Level);
}
internal override void Abort(HTTPConnectionStates newState)
{
State = newState;
switch (State)
{
case HTTPConnectionStates.TimedOut: TimedOutStart = DateTime.UtcNow; break;
}
XHR_Abort(this.NativeId);
}
protected override void ThreadFunc(object param /*null*/)
{
// XmlHttpRequest setup
this.NativeId = XHR_Create(HTTPRequest.MethodNames[(byte)CurrentRequest.MethodType],
CurrentRequest.CurrentUri.OriginalString,
CurrentRequest.Credentials != null ? CurrentRequest.Credentials.UserName : null,
CurrentRequest.Credentials != null ? CurrentRequest.Credentials.Password : null);
Connections.Add(NativeId, this);
CurrentRequest.EnumerateHeaders((header, values) =>
{
if (header != "Content-Length")
for (int i = 0; i < values.Count; ++i)
XHR_SetRequestHeader(NativeId, header, values[i]);
}, /*callBeforeSendCallback:*/ true);
byte[] body = CurrentRequest.GetEntityBody();
XHR_SetResponseHandler(NativeId, WebGLConnection.OnResponse, WebGLConnection.OnError, WebGLConnection.OnTimeout, WebGLConnection.OnAborted);
XHR_SetProgressHandler(NativeId, WebGLConnection.OnDownloadProgress, WebGLConnection.OnUploadProgress);
XHR_SetTimeout(NativeId, (uint)(CurrentRequest.ConnectTimeout.TotalMilliseconds + CurrentRequest.Timeout.TotalMilliseconds));
XHR_Send(NativeId, body, body != null ? body.Length : 0);
}
#region Callback Implementations
void OnResponse(int httpStatus, byte[] buffer)
{
try
{
using (MemoryStream ms = new MemoryStream())
{
Stream = ms;
XHR_GetStatusLine(NativeId, OnBufferCallback);
XHR_GetResponseHeaders(NativeId, OnBufferCallback);
if (buffer != null && buffer.Length > 0)
ms.Write(buffer, 0, buffer.Length);
ms.Seek(0L, SeekOrigin.Begin);
SupportedProtocols protocol = CurrentRequest.ProtocolHandler == SupportedProtocols.Unknown ? HTTPProtocolFactory.GetProtocolFromUri(CurrentRequest.CurrentUri) : CurrentRequest.ProtocolHandler;
CurrentRequest.Response = HTTPProtocolFactory.Get(protocol, CurrentRequest, ms, CurrentRequest.UseStreaming, false);
CurrentRequest.Response.Receive(buffer != null && buffer.Length > 0 ? (int)buffer.Length : -1, true);
}
}
catch (Exception e)
{
HTTPManager.Logger.Exception(this.NativeId + " WebGLConnection", "OnResponse", e);
if (CurrentRequest != null)
{
// Something gone bad, Response must be null!
CurrentRequest.Response = null;
switch (State)
{
case HTTPConnectionStates.AbortRequested:
CurrentRequest.State = HTTPRequestStates.Aborted;
break;
case HTTPConnectionStates.TimedOut:
CurrentRequest.State = HTTPRequestStates.TimedOut;
break;
default:
CurrentRequest.Exception = e;
CurrentRequest.State = HTTPRequestStates.Error;
break;
}
}
}
finally
{
Connections.Remove(NativeId);
Stream = null;
if (CurrentRequest != null)
lock (HTTPManager.Locker)
{
State = HTTPConnectionStates.Closed;
if (CurrentRequest.State == HTTPRequestStates.Processing)
{
if (CurrentRequest.Response != null)
CurrentRequest.State = HTTPRequestStates.Finished;
else
CurrentRequest.State = HTTPRequestStates.Error;
}
}
LastProcessTime = DateTime.UtcNow;
if (OnConnectionRecycled != null)
RecycleNow();
XHR_Release(NativeId);
}
}
void OnBuffer(byte[] buffer)
{
if (Stream != null)
{
Stream.Write(buffer, 0, buffer.Length);
Stream.Write(new byte[2] { HTTPResponse.CR, HTTPResponse.LF }, 0, 2);
}
}
void OnDownloadProgress(int down, int total)
{
CurrentRequest.Downloaded = down;
CurrentRequest.DownloadLength = total;
CurrentRequest.DownloadProgressChanged = true;
}
void OnUploadProgress(int up, int total)
{
CurrentRequest.Uploaded = up;
CurrentRequest.UploadLength = total;
CurrentRequest.UploadProgressChanged = true;
}
void OnError(string error)
{
HTTPManager.Logger.Information(this.NativeId + " WebGLConnection - OnError", error);
Connections.Remove(NativeId);
Stream = null;
if (CurrentRequest != null)
lock (HTTPManager.Locker)
{
State = HTTPConnectionStates.Closed;
CurrentRequest.State = HTTPRequestStates.Error;
CurrentRequest.Exception = new Exception(error);
}
LastProcessTime = DateTime.UtcNow;
if (OnConnectionRecycled != null)
RecycleNow();
XHR_Release(NativeId);
}
void OnTimeout()
{
HTTPManager.Logger.Information(this.NativeId + " WebGLConnection - OnResponse", string.Empty);
Connections.Remove(NativeId);
Stream = null;
if (CurrentRequest != null)
lock (HTTPManager.Locker)
{
State = HTTPConnectionStates.Closed;
CurrentRequest.State = HTTPRequestStates.TimedOut;
}
LastProcessTime = DateTime.UtcNow;
if (OnConnectionRecycled != null)
RecycleNow();
XHR_Release(NativeId);
}
void OnAborted()
{
HTTPManager.Logger.Information(this.NativeId + " WebGLConnection - OnAborted", string.Empty);
Connections.Remove(NativeId);
Stream = null;
if (CurrentRequest != null)
lock (HTTPManager.Locker)
{
State = HTTPConnectionStates.Closed;
CurrentRequest.State = HTTPRequestStates.Aborted;
}
LastProcessTime = DateTime.UtcNow;
if (OnConnectionRecycled != null)
RecycleNow();
XHR_Release(NativeId);
}
#endregion
#region WebGL Static Callbacks
[AOT.MonoPInvokeCallback(typeof(OnWebGLRequestHandlerDelegate))]
static void OnResponse(int nativeId, int httpStatus, IntPtr pBuffer, int length, int err)
{
HTTPManager.Logger.Information("WebGLConnection - OnResponse", string.Format("{0} {1} {2} {3}", nativeId, httpStatus, length, err));
WebGLConnection conn = null;
if (!Connections.TryGetValue(nativeId, out conn))
{
HTTPManager.Logger.Error("WebGLConnection - OnResponse", "No WebGL connection found for nativeId: " + nativeId.ToString());
return;
}
byte[] buffer = new byte[length];
// Copy data from the 'unmanaged' memory to managed memory. Buffer will be reclaimed by the GC.
Marshal.Copy(pBuffer, buffer, 0, length);
conn.OnResponse(httpStatus, buffer);
}
[AOT.MonoPInvokeCallback(typeof(OnWebGLBufferDelegate))]
static void OnBufferCallback(int nativeId, IntPtr pBuffer, int length)
{
WebGLConnection conn = null;
if (!Connections.TryGetValue(nativeId, out conn))
{
HTTPManager.Logger.Error("WebGLConnection - OnBufferCallback", "No WebGL connection found for nativeId: " + nativeId.ToString());
return;
}
byte[] buffer = new byte[length];
// Copy data from the 'unmanaged' memory to managed memory. Buffer will be reclaimed by the GC.
Marshal.Copy(pBuffer, buffer, 0, length);
conn.OnBuffer(buffer);
}
[AOT.MonoPInvokeCallback(typeof(OnWebGLProgressDelegate))]
static void OnDownloadProgress(int nativeId, int downloaded, int total)
{
HTTPManager.Logger.Information(nativeId + " OnDownloadProgress", downloaded.ToString() + " / " + total.ToString());
WebGLConnection conn = null;
if (!Connections.TryGetValue(nativeId, out conn))
{
HTTPManager.Logger.Error("WebGLConnection - OnDownloadProgress", "No WebGL connection found for nativeId: " + nativeId.ToString());
return;
}
conn.OnDownloadProgress(downloaded, total);
}
[AOT.MonoPInvokeCallback(typeof(OnWebGLProgressDelegate))]
static void OnUploadProgress(int nativeId, int uploaded, int total)
{
HTTPManager.Logger.Information(nativeId + " OnUploadProgress", uploaded.ToString() + " / " + total.ToString());
WebGLConnection conn = null;
if (!Connections.TryGetValue(nativeId, out conn))
{
HTTPManager.Logger.Error("WebGLConnection - OnUploadProgress", "No WebGL connection found for nativeId: " + nativeId.ToString());
return;
}
conn.OnUploadProgress(uploaded, total);
}
[AOT.MonoPInvokeCallback(typeof(OnWebGLErrorDelegate))]
static void OnError(int nativeId, string error)
{
WebGLConnection conn = null;
if (!Connections.TryGetValue(nativeId, out conn))
{
HTTPManager.Logger.Error("WebGLConnection - OnError", "No WebGL connection found for nativeId: " + nativeId.ToString() + " Error: " + error);
return;
}
conn.OnError(error);
}
[AOT.MonoPInvokeCallback(typeof(OnWebGLTimeoutDelegate))]
static void OnTimeout(int nativeId)
{
WebGLConnection conn = null;
if (!Connections.TryGetValue(nativeId, out conn))
{
HTTPManager.Logger.Error("WebGLConnection - OnTimeout", "No WebGL connection found for nativeId: " + nativeId.ToString());
return;
}
conn.OnTimeout();
}
[AOT.MonoPInvokeCallback(typeof(OnWebGLAbortedDelegate))]
static void OnAborted(int nativeId)
{
WebGLConnection conn = null;
if (!Connections.TryGetValue(nativeId, out conn))
{
HTTPManager.Logger.Error("WebGLConnection - OnAborted", "No WebGL connection found for nativeId: " + nativeId.ToString());
return;
}
conn.OnAborted();
}
#endregion
#region WebGL Interface
[DllImport("__Internal")]
private static extern int XHR_Create(string method, string url, string userName, string passwd);
/// <summary>
/// Is an unsigned long representing the number of milliseconds a request can take before automatically being terminated. A value of 0 (which is the default) means there is no timeout.
/// </summary>
[DllImport("__Internal")]
private static extern void XHR_SetTimeout(int nativeId, uint timeout);
[DllImport("__Internal")]
private static extern void XHR_SetRequestHeader(int nativeId, string header, string value);
[DllImport("__Internal")]
private static extern void XHR_SetResponseHandler(int nativeId, OnWebGLRequestHandlerDelegate onresponse, OnWebGLErrorDelegate onerror, OnWebGLTimeoutDelegate ontimeout, OnWebGLAbortedDelegate onabort);
[DllImport("__Internal")]
private static extern void XHR_SetProgressHandler(int nativeId, OnWebGLProgressDelegate onDownloadProgress, OnWebGLProgressDelegate onUploadProgress);
[DllImport("__Internal")]
private static extern void XHR_Send(int nativeId, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 2)] byte[] body, int length);
[DllImport("__Internal")]
private static extern void XHR_GetResponseHeaders(int nativeId, OnWebGLBufferDelegate callback);
[DllImport("__Internal")]
private static extern void XHR_GetStatusLine(int nativeId, OnWebGLBufferDelegate callback);
[DllImport("__Internal")]
private static extern void XHR_Abort(int nativeId);
[DllImport("__Internal")]
private static extern void XHR_Release(int nativeId);
[DllImport("__Internal")]
private static extern void XHR_SetLoglevel(int logLevel);
#endregion
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c888ab88487f84e229e82f41bcda24a8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ab956f4a5863d44388cf566a847c61aa
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,358 @@
#if !BESTHTTP_DISABLE_COOKIES && (!UNITY_WEBGL || UNITY_EDITOR)
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; }
#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;
}
#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)((Name != null ? Name.Length * sizeof(char) : 0) +
(Value != null ? Value.Length * sizeof(char) : 0) +
(Domain != null ? Domain.Length * sizeof(char) : 0) +
(Path != null ? Path.Length * sizeof(char) : 0) +
(sizeof(long) * 4) +
(sizeof(bool) * 3));
}
public static Cookie Parse(string header, Uri defaultDomain)
{
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;
default:
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
{
}
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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0aa7201aae8a944d580350b37ceefef5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,484 @@
#if !BESTHTTP_DISABLE_COOKIES && (!UNITY_WEBGL || UNITY_EDITOR)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
#if NETFX_CORE
using FileStream = BestHTTP.PlatformSupport.IO.FileStream;
using Directory = BestHTTP.PlatformSupport.IO.Directory;
using File = BestHTTP.PlatformSupport.IO.File;
using BestHTTP.PlatformSupport.IO;
#else
using FileStream = System.IO.FileStream;
using Directory = System.IO.Directory;
using System.IO;
#endif
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 (IsSupportCheckDone)
return _isSavingSupported;
try
{
File.Exists(HTTPManager.GetRootCacheFolder());
_isSavingSupported = true;
}
catch
{
_isSavingSupported = false;
HTTPManager.Logger.Warning("CookieJar", "Cookie saving and loading disabled!");
}
finally
{
IsSupportCheckDone = true;
}
return _isSavingSupported;
}
}
#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 object Locker = new object();
private static bool _isSavingSupported;
private static bool IsSupportCheckDone;
private static bool Loaded;
#endregion
#region Internal Functions
internal static void SetupFolder()
{
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
{ }
}
/// <summary>
/// Will set or update all cookies from the response object.
/// </summary>
internal static void Set(HTTPResponse response)
{
if (response == null)
return;
lock(Locker)
{
try
{
Maintain();
List<Cookie> newCookies = new List<Cookie>();
var setCookieHeaders = response.GetHeaderValues("set-cookie");
// No cookies. :'(
if (setCookieHeaders == null)
return;
foreach (var cookieHeader in setCookieHeaders)
{
try
{
Cookie cookie = Cookie.Parse(cookieHeader, response.baseRequest.CurrentUri);
if (cookie != null)
{
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
}
}
response.Cookies = newCookies;
}
catch
{}
}
}
/// <summary>
/// Deletes all expired or 'old' cookies, and will keep the sum size of cookies under the given size.
/// </summary>
internal static void Maintain()
{
// It's not the same as in the rfc:
// http://tools.ietf.org/html/rfc6265#section-5.3
lock (Locker)
{
try
{
uint size = 0;
TimeSpan accessThreshold = TimeSpan.FromDays(7);
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
{ }
}
}
/// <summary>
/// Saves the Cookie Jar to a file.
/// </summary>
/// <remarks>Not implemented under Unity WebPlayer</remarks>
internal static void Persist()
{
if (!IsSavingSupported)
return;
lock (Locker)
{
try
{
// Delete any expired cookie
Maintain();
if (!Directory.Exists(CookieFolder))
Directory.CreateDirectory(CookieFolder);
using (var fs = new FileStream(LibraryPath, FileMode.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
{ }
}
}
/// <summary>
/// Load previously persisted cookie library from the file.
/// </summary>
internal static void Load()
{
if (!IsSavingSupported)
return;
lock (Locker)
{
if (Loaded)
return;
SetupFolder();
try
{
Cookies.Clear();
if (!Directory.Exists(CookieFolder))
Directory.CreateDirectory(CookieFolder);
if (!File.Exists(LibraryPath))
return;
using (var fs = new FileStream(LibraryPath, FileMode.Open))
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;
}
}
}
#endregion
#region Public Functions
/// <summary>
/// Returns all Cookies that corresponds to the given Uri.
/// </summary>
public static List<Cookie> Get(Uri uri)
{
lock (Locker)
{
Load();
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 && uri.AbsolutePath.StartsWith(cookie.Path))
{
if (result == null)
result = new List<Cookie>();
result.Add(cookie);
}
}
return result;
}
}
/// <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)
{
lock (Locker)
{
Load();
int idx;
Find(cookie, out idx);
if (idx >= 0)
Cookies[idx] = cookie;
else
Cookies.Add(cookie);
}
}
public static List<Cookie> GetAll()
{
lock (Locker)
{
Load();
return Cookies;
}
}
/// <summary>
/// Deletes all cookies from the Jar.
/// </summary>
public static void Clear()
{
lock (Locker)
{
Load();
Cookies.Clear();
}
}
/// <summary>
/// Removes cookies that older than the given parameter.
/// </summary>
public static void Clear(TimeSpan olderThan)
{
lock (Locker)
{
Load();
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++;
}
}
}
/// <summary>
/// Removes cookies that matches to the given domain.
/// </summary>
public static void Clear(string domain)
{
lock (Locker)
{
Load();
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++;
}
}
}
public static void Remove(Uri uri, string name)
{
lock(Locker)
{
Load();
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++;
}
}
}
#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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ae9aa41306e754581b311c084eebd182
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7856c25942b9c4970817cfe58dc84e56
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,473 @@
// CRC32.cs
// ------------------------------------------------------------------
//
// Copyright (c) 2011 Dino Chiesa.
// All rights reserved.
//
// This code module is part of DotNetZip, a zipfile class library.
//
// ------------------------------------------------------------------
//
// This code is licensed under the Microsoft Public License.
// See the file License.txt for the license details.
// More info on: http://dotnetzip.codeplex.com
//
// ------------------------------------------------------------------
//
// Last Saved: <2011-August-02 18:25:54>
//
// ------------------------------------------------------------------
//
// This module defines the CRC32 class, which can do the CRC32 algorithm, using
// arbitrary starting polynomials, and bit reversal. The bit reversal is what
// distinguishes this CRC-32 used in BZip2 from the CRC-32 that is used in PKZIP
// files, or GZIP files. This class does both.
//
// ------------------------------------------------------------------
using System;
using Interop = System.Runtime.InteropServices;
namespace BestHTTP.Decompression.Crc
{
/// <summary>
/// Computes a CRC-32. The CRC-32 algorithm is parameterized - you
/// can set the polynomial and enable or disable bit
/// reversal. This can be used for GZIP, BZip2, or ZIP.
/// </summary>
/// <remarks>
/// This type is used internally by DotNetZip; it is generally not used
/// directly by applications wishing to create, read, or manipulate zip
/// archive files.
/// </remarks>
internal class CRC32
{
/// <summary>
/// Indicates the total number of bytes applied to the CRC.
/// </summary>
public Int64 TotalBytesRead
{
get
{
return _TotalBytesRead;
}
}
/// <summary>
/// Indicates the current CRC for all blocks slurped in.
/// </summary>
public Int32 Crc32Result
{
get
{
return unchecked((Int32)(~_register));
}
}
/// <summary>
/// Returns the CRC32 for the specified stream.
/// </summary>
/// <param name="input">The stream over which to calculate the CRC32</param>
/// <returns>the CRC32 calculation</returns>
public Int32 GetCrc32(System.IO.Stream input)
{
return GetCrc32AndCopy(input, null);
}
/// <summary>
/// Returns the CRC32 for the specified stream, and writes the input into the
/// output stream.
/// </summary>
/// <param name="input">The stream over which to calculate the CRC32</param>
/// <param name="output">The stream into which to deflate the input</param>
/// <returns>the CRC32 calculation</returns>
public Int32 GetCrc32AndCopy(System.IO.Stream input, System.IO.Stream output)
{
if (input == null)
throw new Exception("The input stream must not be null.");
unchecked
{
byte[] buffer = new byte[BUFFER_SIZE];
int readSize = BUFFER_SIZE;
_TotalBytesRead = 0;
int count = input.Read(buffer, 0, readSize);
if (output != null) output.Write(buffer, 0, count);
_TotalBytesRead += count;
while (count > 0)
{
SlurpBlock(buffer, 0, count);
count = input.Read(buffer, 0, readSize);
if (output != null) output.Write(buffer, 0, count);
_TotalBytesRead += count;
}
return (Int32)(~_register);
}
}
/// <summary>
/// Get the CRC32 for the given (word,byte) combo. This is a
/// computation defined by PKzip for PKZIP 2.0 (weak) encryption.
/// </summary>
/// <param name="W">The word to start with.</param>
/// <param name="B">The byte to combine it with.</param>
/// <returns>The CRC-ized result.</returns>
public Int32 ComputeCrc32(Int32 W, byte B)
{
return _InternalComputeCrc32((UInt32)W, B);
}
internal Int32 _InternalComputeCrc32(UInt32 W, byte B)
{
return (Int32)(crc32Table[(W ^ B) & 0xFF] ^ (W >> 8));
}
/// <summary>
/// Update the value for the running CRC32 using the given block of bytes.
/// This is useful when using the CRC32() class in a Stream.
/// </summary>
/// <param name="block">block of bytes to slurp</param>
/// <param name="offset">starting point in the block</param>
/// <param name="count">how many bytes within the block to slurp</param>
public void SlurpBlock(byte[] block, int offset, int count)
{
if (block == null)
throw new Exception("The data buffer must not be null.");
// bzip algorithm
for (int i = 0; i < count; i++)
{
int x = offset + i;
byte b = block[x];
if (this.reverseBits)
{
UInt32 temp = (_register >> 24) ^ b;
_register = (_register << 8) ^ crc32Table[temp];
}
else
{
UInt32 temp = (_register & 0x000000FF) ^ b;
_register = (_register >> 8) ^ crc32Table[temp];
}
}
_TotalBytesRead += count;
}
/// <summary>
/// Process one byte in the CRC.
/// </summary>
/// <param name = "b">the byte to include into the CRC . </param>
public void UpdateCRC(byte b)
{
if (this.reverseBits)
{
UInt32 temp = (_register >> 24) ^ b;
_register = (_register << 8) ^ crc32Table[temp];
}
else
{
UInt32 temp = (_register & 0x000000FF) ^ b;
_register = (_register >> 8) ^ crc32Table[temp];
}
}
/// <summary>
/// Process a run of N identical bytes into the CRC.
/// </summary>
/// <remarks>
/// <para>
/// This method serves as an optimization for updating the CRC when a
/// run of identical bytes is found. Rather than passing in a buffer of
/// length n, containing all identical bytes b, this method accepts the
/// byte value and the length of the (virtual) buffer - the length of
/// the run.
/// </para>
/// </remarks>
/// <param name = "b">the byte to include into the CRC. </param>
/// <param name = "n">the number of times that byte should be repeated. </param>
public void UpdateCRC(byte b, int n)
{
while (n-- > 0)
{
if (this.reverseBits)
{
uint temp = (_register >> 24) ^ b;
_register = (_register << 8) ^ crc32Table[(temp >= 0)
? temp
: (temp + 256)];
}
else
{
UInt32 temp = (_register & 0x000000FF) ^ b;
_register = (_register >> 8) ^ crc32Table[(temp >= 0)
? temp
: (temp + 256)];
}
}
}
private static uint ReverseBits(uint data)
{
unchecked
{
uint ret = data;
ret = (ret & 0x55555555) << 1 | (ret >> 1) & 0x55555555;
ret = (ret & 0x33333333) << 2 | (ret >> 2) & 0x33333333;
ret = (ret & 0x0F0F0F0F) << 4 | (ret >> 4) & 0x0F0F0F0F;
ret = (ret << 24) | ((ret & 0xFF00) << 8) | ((ret >> 8) & 0xFF00) | (ret >> 24);
return ret;
}
}
private static byte ReverseBits(byte data)
{
unchecked
{
uint u = (uint)data * 0x00020202;
uint m = 0x01044010;
uint s = u & m;
uint t = (u << 2) & (m << 1);
return (byte)((0x01001001 * (s + t)) >> 24);
}
}
private void GenerateLookupTable()
{
crc32Table = new UInt32[256];
unchecked
{
UInt32 dwCrc;
byte i = 0;
do
{
dwCrc = i;
for (byte j = 8; j > 0; j--)
{
if ((dwCrc & 1) == 1)
{
dwCrc = (dwCrc >> 1) ^ dwPolynomial;
}
else
{
dwCrc >>= 1;
}
}
if (reverseBits)
{
crc32Table[ReverseBits(i)] = ReverseBits(dwCrc);
}
else
{
crc32Table[i] = dwCrc;
}
i++;
} while (i!=0);
}
#if VERBOSE
Console.WriteLine();
Console.WriteLine("private static readonly UInt32[] crc32Table = {");
for (int i = 0; i < crc32Table.Length; i+=4)
{
Console.Write(" ");
for (int j=0; j < 4; j++)
{
Console.Write(" 0x{0:X8}U,", crc32Table[i+j]);
}
Console.WriteLine();
}
Console.WriteLine("};");
Console.WriteLine();
#endif
}
private uint gf2_matrix_times(uint[] matrix, uint vec)
{
uint sum = 0;
int i=0;
while (vec != 0)
{
if ((vec & 0x01)== 0x01)
sum ^= matrix[i];
vec >>= 1;
i++;
}
return sum;
}
private void gf2_matrix_square(uint[] square, uint[] mat)
{
for (int i = 0; i < 32; i++)
square[i] = gf2_matrix_times(mat, mat[i]);
}
/// <summary>
/// Combines the given CRC32 value with the current running total.
/// </summary>
/// <remarks>
/// This is useful when using a divide-and-conquer approach to
/// calculating a CRC. Multiple threads can each calculate a
/// CRC32 on a segment of the data, and then combine the
/// individual CRC32 values at the end.
/// </remarks>
/// <param name="crc">the crc value to be combined with this one</param>
/// <param name="length">the length of data the CRC value was calculated on</param>
public void Combine(int crc, int length)
{
uint[] even = new uint[32]; // even-power-of-two zeros operator
uint[] odd = new uint[32]; // odd-power-of-two zeros operator
if (length == 0)
return;
uint crc1= ~_register;
uint crc2= (uint) crc;
// put operator for one zero bit in odd
odd[0] = this.dwPolynomial; // the CRC-32 polynomial
uint row = 1;
for (int i = 1; i < 32; i++)
{
odd[i] = row;
row <<= 1;
}
// put operator for two zero bits in even
gf2_matrix_square(even, odd);
// put operator for four zero bits in odd
gf2_matrix_square(odd, even);
uint len2 = (uint) length;
// apply len2 zeros to crc1 (first square will put the operator for one
// zero byte, eight zero bits, in even)
do {
// apply zeros operator for this bit of len2
gf2_matrix_square(even, odd);
if ((len2 & 1)== 1)
crc1 = gf2_matrix_times(even, crc1);
len2 >>= 1;
if (len2 == 0)
break;
// another iteration of the loop with odd and even swapped
gf2_matrix_square(odd, even);
if ((len2 & 1)==1)
crc1 = gf2_matrix_times(odd, crc1);
len2 >>= 1;
} while (len2 != 0);
crc1 ^= crc2;
_register= ~crc1;
//return (int) crc1;
return;
}
/// <summary>
/// Create an instance of the CRC32 class using the default settings: no
/// bit reversal, and a polynomial of 0xEDB88320.
/// </summary>
public CRC32() : this(false)
{
}
/// <summary>
/// Create an instance of the CRC32 class, specifying whether to reverse
/// data bits or not.
/// </summary>
/// <param name='reverseBits'>
/// specify true if the instance should reverse data bits.
/// </param>
/// <remarks>
/// <para>
/// In the CRC-32 used by BZip2, the bits are reversed. Therefore if you
/// want a CRC32 with compatibility with BZip2, you should pass true
/// here. In the CRC-32 used by GZIP and PKZIP, the bits are not
/// reversed; Therefore if you want a CRC32 with compatibility with
/// those, you should pass false.
/// </para>
/// </remarks>
public CRC32(bool reverseBits) :
this( unchecked((int)0xEDB88320), reverseBits)
{
}
/// <summary>
/// Create an instance of the CRC32 class, specifying the polynomial and
/// whether to reverse data bits or not.
/// </summary>
/// <param name='polynomial'>
/// The polynomial to use for the CRC, expressed in the reversed (LSB)
/// format: the highest ordered bit in the polynomial value is the
/// coefficient of the 0th power; the second-highest order bit is the
/// coefficient of the 1 power, and so on. Expressed this way, the
/// polynomial for the CRC-32C used in IEEE 802.3, is 0xEDB88320.
/// </param>
/// <param name='reverseBits'>
/// specify true if the instance should reverse data bits.
/// </param>
///
/// <remarks>
/// <para>
/// In the CRC-32 used by BZip2, the bits are reversed. Therefore if you
/// want a CRC32 with compatibility with BZip2, you should pass true
/// here for the <c>reverseBits</c> parameter. In the CRC-32 used by
/// GZIP and PKZIP, the bits are not reversed; Therefore if you want a
/// CRC32 with compatibility with those, you should pass false for the
/// <c>reverseBits</c> parameter.
/// </para>
/// </remarks>
public CRC32(int polynomial, bool reverseBits)
{
this.reverseBits = reverseBits;
this.dwPolynomial = (uint) polynomial;
this.GenerateLookupTable();
}
/// <summary>
/// Reset the CRC-32 class - clear the CRC "remainder register."
/// </summary>
/// <remarks>
/// <para>
/// Use this when employing a single instance of this class to compute
/// multiple, distinct CRCs on multiple, distinct data blocks.
/// </para>
/// </remarks>
public void Reset()
{
_register = 0xFFFFFFFFU;
}
// private member vars
private UInt32 dwPolynomial;
private Int64 _TotalBytesRead;
private bool reverseBits;
private UInt32[] crc32Table;
private const int BUFFER_SIZE = 8192;
private UInt32 _register = 0xFFFFFFFFU;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d00d68e6664154b5e8cb1836aad7a83e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dbe0203724b064747a89747479467cd7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,804 @@
// DeflateStream.cs
// ------------------------------------------------------------------
//
// Copyright (c) 2009-2010 Dino Chiesa.
// All rights reserved.
//
// This code module is part of DotNetZip, a zipfile class library.
//
// ------------------------------------------------------------------
//
// This code is licensed under the Microsoft Public License.
// See the file License.txt for the license details.
// More info on: http://dotnetzip.codeplex.com
//
// ------------------------------------------------------------------
//
// last saved (in emacs):
// Time-stamp: <2011-July-31 14:48:11>
//
// ------------------------------------------------------------------
//
// This module defines the DeflateStream class, which can be used as a replacement for
// the System.IO.Compression.DeflateStream class in the .NET BCL.
//
// ------------------------------------------------------------------
using System;
namespace BestHTTP.Decompression.Zlib
{
/// <summary>
/// A class for compressing and decompressing streams using the Deflate algorithm.
/// </summary>
///
/// <remarks>
///
/// <para>
/// The DeflateStream is a <see
/// href="http://en.wikipedia.org/wiki/Decorator_pattern">Decorator</see> on a <see
/// cref="System.IO.Stream"/>. It adds DEFLATE compression or decompression to any
/// stream.
/// </para>
///
/// <para>
/// Using this stream, applications can compress or decompress data via stream
/// <c>Read</c> and <c>Write</c> operations. Either compresssion or decompression
/// can occur through either reading or writing. The compression format used is
/// DEFLATE, which is documented in <see
/// href="http://www.ietf.org/rfc/rfc1951.txt">IETF RFC 1951</see>, "DEFLATE
/// Compressed Data Format Specification version 1.3.".
/// </para>
///
/// </remarks>
///
/// <seealso cref="GZipStream" />
internal class DeflateStream : System.IO.Stream
{
internal ZlibBaseStream _baseStream;
internal System.IO.Stream _innerStream;
bool _disposed;
/// <summary>
/// Create a DeflateStream using the specified CompressionMode.
/// </summary>
///
/// <remarks>
/// When mode is <c>CompressionMode.Compress</c>, the DeflateStream will use
/// the default compression level. The "captive" stream will be closed when
/// the DeflateStream is closed.
/// </remarks>
///
/// <example>
/// This example uses a DeflateStream to compress data from a file, and writes
/// the compressed data to another file.
/// <code>
/// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
/// {
/// using (var raw = System.IO.File.Create(fileToCompress + ".deflated"))
/// {
/// using (Stream compressor = new DeflateStream(raw, CompressionMode.Compress))
/// {
/// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
/// int n;
/// while ((n= input.Read(buffer, 0, buffer.Length)) != 0)
/// {
/// compressor.Write(buffer, 0, n);
/// }
/// }
/// }
/// }
/// </code>
///
/// <code lang="VB">
/// Using input As Stream = File.OpenRead(fileToCompress)
/// Using raw As FileStream = File.Create(fileToCompress &amp; ".deflated")
/// Using compressor As Stream = New DeflateStream(raw, CompressionMode.Compress)
/// Dim buffer As Byte() = New Byte(4096) {}
/// Dim n As Integer = -1
/// Do While (n &lt;&gt; 0)
/// If (n &gt; 0) Then
/// compressor.Write(buffer, 0, n)
/// End If
/// n = input.Read(buffer, 0, buffer.Length)
/// Loop
/// End Using
/// End Using
/// End Using
/// </code>
/// </example>
/// <param name="stream">The stream which will be read or written.</param>
/// <param name="mode">Indicates whether the DeflateStream will compress or decompress.</param>
public DeflateStream(System.IO.Stream stream, CompressionMode mode)
: this(stream, mode, CompressionLevel.Default, false)
{
}
/// <summary>
/// Create a DeflateStream using the specified CompressionMode and the specified CompressionLevel.
/// </summary>
///
/// <remarks>
///
/// <para>
/// When mode is <c>CompressionMode.Decompress</c>, the level parameter is
/// ignored. The "captive" stream will be closed when the DeflateStream is
/// closed.
/// </para>
///
/// </remarks>
///
/// <example>
///
/// This example uses a DeflateStream to compress data from a file, and writes
/// the compressed data to another file.
///
/// <code>
/// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
/// {
/// using (var raw = System.IO.File.Create(fileToCompress + ".deflated"))
/// {
/// using (Stream compressor = new DeflateStream(raw,
/// CompressionMode.Compress,
/// CompressionLevel.BestCompression))
/// {
/// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
/// int n= -1;
/// while (n != 0)
/// {
/// if (n &gt; 0)
/// compressor.Write(buffer, 0, n);
/// n= input.Read(buffer, 0, buffer.Length);
/// }
/// }
/// }
/// }
/// </code>
///
/// <code lang="VB">
/// Using input As Stream = File.OpenRead(fileToCompress)
/// Using raw As FileStream = File.Create(fileToCompress &amp; ".deflated")
/// Using compressor As Stream = New DeflateStream(raw, CompressionMode.Compress, CompressionLevel.BestCompression)
/// Dim buffer As Byte() = New Byte(4096) {}
/// Dim n As Integer = -1
/// Do While (n &lt;&gt; 0)
/// If (n &gt; 0) Then
/// compressor.Write(buffer, 0, n)
/// End If
/// n = input.Read(buffer, 0, buffer.Length)
/// Loop
/// End Using
/// End Using
/// End Using
/// </code>
/// </example>
/// <param name="stream">The stream to be read or written while deflating or inflating.</param>
/// <param name="mode">Indicates whether the <c>DeflateStream</c> will compress or decompress.</param>
/// <param name="level">A tuning knob to trade speed for effectiveness.</param>
public DeflateStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level)
: this(stream, mode, level, false)
{
}
/// <summary>
/// Create a <c>DeflateStream</c> using the specified
/// <c>CompressionMode</c>, and explicitly specify whether the
/// stream should be left open after Deflation or Inflation.
/// </summary>
///
/// <remarks>
///
/// <para>
/// This constructor allows the application to request that the captive stream
/// remain open after the deflation or inflation occurs. By default, after
/// <c>Close()</c> is called on the stream, the captive stream is also
/// closed. In some cases this is not desired, for example if the stream is a
/// memory stream that will be re-read after compression. Specify true for
/// the <paramref name="leaveOpen"/> parameter to leave the stream open.
/// </para>
///
/// <para>
/// The <c>DeflateStream</c> will use the default compression level.
/// </para>
///
/// <para>
/// See the other overloads of this constructor for example code.
/// </para>
/// </remarks>
///
/// <param name="stream">
/// The stream which will be read or written. This is called the
/// "captive" stream in other places in this documentation.
/// </param>
///
/// <param name="mode">
/// Indicates whether the <c>DeflateStream</c> will compress or decompress.
/// </param>
///
/// <param name="leaveOpen">true if the application would like the stream to
/// remain open after inflation/deflation.</param>
public DeflateStream(System.IO.Stream stream, CompressionMode mode, bool leaveOpen)
: this(stream, mode, CompressionLevel.Default, leaveOpen)
{
}
/// <summary>
/// Create a <c>DeflateStream</c> using the specified <c>CompressionMode</c>
/// and the specified <c>CompressionLevel</c>, and explicitly specify whether
/// the stream should be left open after Deflation or Inflation.
/// </summary>
///
/// <remarks>
///
/// <para>
/// When mode is <c>CompressionMode.Decompress</c>, the level parameter is ignored.
/// </para>
///
/// <para>
/// This constructor allows the application to request that the captive stream
/// remain open after the deflation or inflation occurs. By default, after
/// <c>Close()</c> is called on the stream, the captive stream is also
/// closed. In some cases this is not desired, for example if the stream is a
/// <see cref="System.IO.MemoryStream"/> that will be re-read after
/// compression. Specify true for the <paramref name="leaveOpen"/> parameter
/// to leave the stream open.
/// </para>
///
/// </remarks>
///
/// <example>
///
/// This example shows how to use a <c>DeflateStream</c> to compress data from
/// a file, and store the compressed data into another file.
///
/// <code>
/// using (var output = System.IO.File.Create(fileToCompress + ".deflated"))
/// {
/// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
/// {
/// using (Stream compressor = new DeflateStream(output, CompressionMode.Compress, CompressionLevel.BestCompression, true))
/// {
/// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
/// int n= -1;
/// while (n != 0)
/// {
/// if (n &gt; 0)
/// compressor.Write(buffer, 0, n);
/// n= input.Read(buffer, 0, buffer.Length);
/// }
/// }
/// }
/// // can write additional data to the output stream here
/// }
/// </code>
///
/// <code lang="VB">
/// Using output As FileStream = File.Create(fileToCompress &amp; ".deflated")
/// Using input As Stream = File.OpenRead(fileToCompress)
/// Using compressor As Stream = New DeflateStream(output, CompressionMode.Compress, CompressionLevel.BestCompression, True)
/// Dim buffer As Byte() = New Byte(4096) {}
/// Dim n As Integer = -1
/// Do While (n &lt;&gt; 0)
/// If (n &gt; 0) Then
/// compressor.Write(buffer, 0, n)
/// End If
/// n = input.Read(buffer, 0, buffer.Length)
/// Loop
/// End Using
/// End Using
/// ' can write additional data to the output stream here.
/// End Using
/// </code>
/// </example>
/// <param name="stream">The stream which will be read or written.</param>
/// <param name="mode">Indicates whether the DeflateStream will compress or decompress.</param>
/// <param name="leaveOpen">true if the application would like the stream to remain open after inflation/deflation.</param>
/// <param name="level">A tuning knob to trade speed for effectiveness.</param>
public DeflateStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level, bool leaveOpen)
{
_innerStream = stream;
_baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.DEFLATE, leaveOpen);
}
/// <summary>
/// Create a <c>DeflateStream</c> using the specified <c>CompressionMode</c>
/// and the specified <c>CompressionLevel</c>, and explicitly specify whether
/// the stream should be left open after Deflation or Inflation.
/// </summary>
///
/// <remarks>
///
/// <para>
/// When mode is <c>CompressionMode.Decompress</c>, the level parameter is ignored.
/// </para>
///
/// <para>
/// This constructor allows the application to request that the captive stream
/// remain open after the deflation or inflation occurs. By default, after
/// <c>Close()</c> is called on the stream, the captive stream is also
/// closed. In some cases this is not desired, for example if the stream is a
/// <see cref="System.IO.MemoryStream"/> that will be re-read after
/// compression. Specify true for the <paramref name="leaveOpen"/> parameter
/// to leave the stream open.
/// </para>
///
/// </remarks>
///
/// <example>
///
/// This example shows how to use a <c>DeflateStream</c> to compress data from
/// a file, and store the compressed data into another file.
///
/// <code>
/// using (var output = System.IO.File.Create(fileToCompress + ".deflated"))
/// {
/// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
/// {
/// using (Stream compressor = new DeflateStream(output, CompressionMode.Compress, CompressionLevel.BestCompression, true))
/// {
/// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
/// int n= -1;
/// while (n != 0)
/// {
/// if (n &gt; 0)
/// compressor.Write(buffer, 0, n);
/// n= input.Read(buffer, 0, buffer.Length);
/// }
/// }
/// }
/// // can write additional data to the output stream here
/// }
/// </code>
///
/// <code lang="VB">
/// Using output As FileStream = File.Create(fileToCompress &amp; ".deflated")
/// Using input As Stream = File.OpenRead(fileToCompress)
/// Using compressor As Stream = New DeflateStream(output, CompressionMode.Compress, CompressionLevel.BestCompression, True)
/// Dim buffer As Byte() = New Byte(4096) {}
/// Dim n As Integer = -1
/// Do While (n &lt;&gt; 0)
/// If (n &gt; 0) Then
/// compressor.Write(buffer, 0, n)
/// End If
/// n = input.Read(buffer, 0, buffer.Length)
/// Loop
/// End Using
/// End Using
/// ' can write additional data to the output stream here.
/// End Using
/// </code>
/// </example>
/// <param name="stream">The stream which will be read or written.</param>
/// <param name="mode">Indicates whether the DeflateStream will compress or decompress.</param>
/// <param name="leaveOpen">true if the application would like the stream to remain open after inflation/deflation.</param>
/// <param name="level">A tuning knob to trade speed for effectiveness.</param>
/// <param name="windowBits">Desired window bits.</param>
public DeflateStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level, bool leaveOpen, int windowBits)
{
_innerStream = stream;
_baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.DEFLATE, leaveOpen, windowBits);
}
#region Zlib properties
/// <summary>
/// This property sets the flush behavior on the stream.
/// </summary>
/// <remarks> See the ZLIB documentation for the meaning of the flush behavior.
/// </remarks>
virtual public FlushType FlushMode
{
get { return (this._baseStream._flushMode); }
set
{
if (_disposed) throw new ObjectDisposedException("DeflateStream");
this._baseStream._flushMode = value;
}
}
/// <summary>
/// The size of the working buffer for the compression codec.
/// </summary>
///
/// <remarks>
/// <para>
/// The working buffer is used for all stream operations. The default size is
/// 1024 bytes. The minimum size is 128 bytes. You may get better performance
/// with a larger buffer. Then again, you might not. You would have to test
/// it.
/// </para>
///
/// <para>
/// Set this before the first call to <c>Read()</c> or <c>Write()</c> on the
/// stream. If you try to set it afterwards, it will throw.
/// </para>
/// </remarks>
public int BufferSize
{
get
{
return this._baseStream._bufferSize;
}
set
{
if (_disposed) throw new ObjectDisposedException("DeflateStream");
if (this._baseStream._workingBuffer != null)
throw new ZlibException("The working buffer is already set.");
if (value < ZlibConstants.WorkingBufferSizeMin)
throw new ZlibException(String.Format("Don't be silly. {0} bytes?? Use a bigger buffer, at least {1}.", value, ZlibConstants.WorkingBufferSizeMin));
this._baseStream._bufferSize = value;
}
}
/// <summary>
/// The ZLIB strategy to be used during compression.
/// </summary>
///
/// <remarks>
/// By tweaking this parameter, you may be able to optimize the compression for
/// data with particular characteristics.
/// </remarks>
public CompressionStrategy Strategy
{
get
{
return this._baseStream.Strategy;
}
set
{
if (_disposed) throw new ObjectDisposedException("DeflateStream");
this._baseStream.Strategy = value;
}
}
/// <summary> Returns the total number of bytes input so far.</summary>
virtual public long TotalIn
{
get
{
return this._baseStream._z.TotalBytesIn;
}
}
/// <summary> Returns the total number of bytes output so far.</summary>
virtual public long TotalOut
{
get
{
return this._baseStream._z.TotalBytesOut;
}
}
#endregion
#region System.IO.Stream methods
/// <summary>
/// Dispose the stream.
/// </summary>
/// <remarks>
/// <para>
/// This may or may not result in a <c>Close()</c> call on the captive
/// stream. See the constructors that have a <c>leaveOpen</c> parameter
/// for more information.
/// </para>
/// <para>
/// Application code won't call this code directly. This method may be
/// invoked in two distinct scenarios. If disposing == true, the method
/// has been called directly or indirectly by a user's code, for example
/// via the public Dispose() method. In this case, both managed and
/// unmanaged resources can be referenced and disposed. If disposing ==
/// false, the method has been called by the runtime from inside the
/// object finalizer and this method should not reference other objects;
/// in that case only unmanaged resources must be referenced or
/// disposed.
/// </para>
/// </remarks>
/// <param name="disposing">
/// true if the Dispose method was invoked by user code.
/// </param>
protected override void Dispose(bool disposing)
{
try
{
if (!_disposed)
{
if (disposing && (this._baseStream != null))
this._baseStream.Close();
_disposed = true;
}
}
finally
{
base.Dispose(disposing);
}
}
/// <summary>
/// Indicates whether the stream can be read.
/// </summary>
/// <remarks>
/// The return value depends on whether the captive stream supports reading.
/// </remarks>
public override bool CanRead
{
get
{
if (_disposed) throw new ObjectDisposedException("DeflateStream");
return _baseStream._stream.CanRead;
}
}
/// <summary>
/// Indicates whether the stream supports Seek operations.
/// </summary>
/// <remarks>
/// Always returns false.
/// </remarks>
public override bool CanSeek
{
get { return false; }
}
/// <summary>
/// Indicates whether the stream can be written.
/// </summary>
/// <remarks>
/// The return value depends on whether the captive stream supports writing.
/// </remarks>
public override bool CanWrite
{
get
{
if (_disposed) throw new ObjectDisposedException("DeflateStream");
return _baseStream._stream.CanWrite;
}
}
/// <summary>
/// Flush the stream.
/// </summary>
public override void Flush()
{
if (_disposed) throw new ObjectDisposedException("DeflateStream");
_baseStream.Flush();
}
/// <summary>
/// Reading this property always throws a <see cref="NotImplementedException"/>.
/// </summary>
public override long Length
{
get { throw new NotImplementedException(); }
}
/// <summary>
/// The position of the stream pointer.
/// </summary>
///
/// <remarks>
/// Setting this property always throws a <see
/// cref="NotImplementedException"/>. Reading will return the total bytes
/// written out, if used in writing, or the total bytes read in, if used in
/// reading. The count may refer to compressed bytes or uncompressed bytes,
/// depending on how you've used the stream.
/// </remarks>
public override long Position
{
get
{
if (this._baseStream._streamMode == BestHTTP.Decompression.Zlib.ZlibBaseStream.StreamMode.Writer)
return this._baseStream._z.TotalBytesOut;
if (this._baseStream._streamMode == BestHTTP.Decompression.Zlib.ZlibBaseStream.StreamMode.Reader)
return this._baseStream._z.TotalBytesIn;
return 0;
}
set { throw new NotImplementedException(); }
}
/// <summary>
/// Read data from the stream.
/// </summary>
/// <remarks>
///
/// <para>
/// If you wish to use the <c>DeflateStream</c> to compress data while
/// reading, you can create a <c>DeflateStream</c> with
/// <c>CompressionMode.Compress</c>, providing an uncompressed data stream.
/// Then call Read() on that <c>DeflateStream</c>, and the data read will be
/// compressed as you read. If you wish to use the <c>DeflateStream</c> to
/// decompress data while reading, you can create a <c>DeflateStream</c> with
/// <c>CompressionMode.Decompress</c>, providing a readable compressed data
/// stream. Then call Read() on that <c>DeflateStream</c>, and the data read
/// will be decompressed as you read.
/// </para>
///
/// <para>
/// A <c>DeflateStream</c> can be used for <c>Read()</c> or <c>Write()</c>, but not both.
/// </para>
///
/// </remarks>
/// <param name="buffer">The buffer into which the read data should be placed.</param>
/// <param name="offset">the offset within that data array to put the first byte read.</param>
/// <param name="count">the number of bytes to read.</param>
/// <returns>the number of bytes actually read</returns>
public override int Read(byte[] buffer, int offset, int count)
{
if (_disposed) throw new ObjectDisposedException("DeflateStream");
return _baseStream.Read(buffer, offset, count);
}
/// <summary>
/// Calling this method always throws a <see cref="NotImplementedException"/>.
/// </summary>
/// <param name="offset">this is irrelevant, since it will always throw!</param>
/// <param name="origin">this is irrelevant, since it will always throw!</param>
/// <returns>irrelevant!</returns>
public override long Seek(long offset, System.IO.SeekOrigin origin)
{
throw new NotImplementedException();
}
/// <summary>
/// Will call the base stream's SetLength method.
/// </summary>
public override void SetLength(long value)
{
_baseStream.SetLength(value);
}
/// <summary>
/// Write data to the stream.
/// </summary>
/// <remarks>
///
/// <para>
/// If you wish to use the <c>DeflateStream</c> to compress data while
/// writing, you can create a <c>DeflateStream</c> with
/// <c>CompressionMode.Compress</c>, and a writable output stream. Then call
/// <c>Write()</c> on that <c>DeflateStream</c>, providing uncompressed data
/// as input. The data sent to the output stream will be the compressed form
/// of the data written. If you wish to use the <c>DeflateStream</c> to
/// decompress data while writing, you can create a <c>DeflateStream</c> with
/// <c>CompressionMode.Decompress</c>, and a writable output stream. Then
/// call <c>Write()</c> on that stream, providing previously compressed
/// data. The data sent to the output stream will be the decompressed form of
/// the data written.
/// </para>
///
/// <para>
/// A <c>DeflateStream</c> can be used for <c>Read()</c> or <c>Write()</c>,
/// but not both.
/// </para>
///
/// </remarks>
///
/// <param name="buffer">The buffer holding data to write to the stream.</param>
/// <param name="offset">the offset within that data array to find the first byte to write.</param>
/// <param name="count">the number of bytes to write.</param>
public override void Write(byte[] buffer, int offset, int count)
{
if (_disposed) throw new ObjectDisposedException("DeflateStream");
_baseStream.Write(buffer, offset, count);
}
#endregion
/// <summary>
/// Compress a string into a byte array using DEFLATE (RFC 1951).
/// </summary>
///
/// <remarks>
/// Uncompress it with <see cref="DeflateStream.UncompressString(byte[])"/>.
/// </remarks>
///
/// <seealso cref="DeflateStream.UncompressString(byte[])">DeflateStream.UncompressString(byte[])</seealso>
/// <seealso cref="DeflateStream.CompressBuffer(byte[])">DeflateStream.CompressBuffer(byte[])</seealso>
/// <seealso cref="GZipStream.CompressString(string)">GZipStream.CompressString(string)</seealso>
///
/// <param name="s">
/// A string to compress. The string will first be encoded
/// using UTF8, then compressed.
/// </param>
///
/// <returns>The string in compressed form</returns>
public static byte[] CompressString(String s)
{
using (var ms = new System.IO.MemoryStream())
{
System.IO.Stream compressor =
new DeflateStream(ms, CompressionMode.Compress, CompressionLevel.BestCompression);
ZlibBaseStream.CompressString(s, compressor);
return ms.ToArray();
}
}
/// <summary>
/// Compress a byte array into a new byte array using DEFLATE.
/// </summary>
///
/// <remarks>
/// Uncompress it with <see cref="DeflateStream.UncompressBuffer(byte[])"/>.
/// </remarks>
///
/// <seealso cref="DeflateStream.CompressString(string)">DeflateStream.CompressString(string)</seealso>
/// <seealso cref="DeflateStream.UncompressBuffer(byte[])">DeflateStream.UncompressBuffer(byte[])</seealso>
/// <seealso cref="GZipStream.CompressBuffer(byte[])">GZipStream.CompressBuffer(byte[])</seealso>
///
/// <param name="b">
/// A buffer to compress.
/// </param>
///
/// <returns>The data in compressed form</returns>
public static byte[] CompressBuffer(byte[] b)
{
using (var ms = new System.IO.MemoryStream())
{
System.IO.Stream compressor =
new DeflateStream( ms, CompressionMode.Compress, CompressionLevel.BestCompression );
ZlibBaseStream.CompressBuffer(b, compressor);
return ms.ToArray();
}
}
/// <summary>
/// Uncompress a DEFLATE'd byte array into a single string.
/// </summary>
///
/// <seealso cref="DeflateStream.CompressString(String)">DeflateStream.CompressString(String)</seealso>
/// <seealso cref="DeflateStream.UncompressBuffer(byte[])">DeflateStream.UncompressBuffer(byte[])</seealso>
/// <seealso cref="GZipStream.UncompressString(byte[])">GZipStream.UncompressString(byte[])</seealso>
///
/// <param name="compressed">
/// A buffer containing DEFLATE-compressed data.
/// </param>
///
/// <returns>The uncompressed string</returns>
public static String UncompressString(byte[] compressed)
{
using (var input = new System.IO.MemoryStream(compressed))
{
System.IO.Stream decompressor =
new DeflateStream(input, CompressionMode.Decompress);
return ZlibBaseStream.UncompressString(compressed, decompressor);
}
}
/// <summary>
/// Uncompress a DEFLATE'd byte array into a byte array.
/// </summary>
///
/// <seealso cref="DeflateStream.CompressBuffer(byte[])">DeflateStream.CompressBuffer(byte[])</seealso>
/// <seealso cref="DeflateStream.UncompressString(byte[])">DeflateStream.UncompressString(byte[])</seealso>
/// <seealso cref="GZipStream.UncompressBuffer(byte[])">GZipStream.UncompressBuffer(byte[])</seealso>
///
/// <param name="compressed">
/// A buffer containing data that has been compressed with DEFLATE.
/// </param>
///
/// <returns>The data in uncompressed form</returns>
public static byte[] UncompressBuffer(byte[] compressed)
{
using (var input = new System.IO.MemoryStream(compressed))
{
System.IO.Stream decompressor = new DeflateStream( input, CompressionMode.Decompress );
return ZlibBaseStream.UncompressBuffer(compressed, decompressor);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 179546e9285584699af2aacb3122966d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 529c3c717ba2043c08e7f83ccd892fc2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,436 @@
// Inftree.cs
// ------------------------------------------------------------------
//
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
// All rights reserved.
//
// This code module is part of DotNetZip, a zipfile class library.
//
// ------------------------------------------------------------------
//
// This code is licensed under the Microsoft Public License.
// See the file License.txt for the license details.
// More info on: http://dotnetzip.codeplex.com
//
// ------------------------------------------------------------------
//
// last saved (in emacs):
// Time-stamp: <2009-October-28 12:43:54>
//
// ------------------------------------------------------------------
//
// This module defines classes used in decompression. This code is derived
// from the jzlib implementation of zlib. In keeping with the license for jzlib,
// the copyright to that code is below.
//
// ------------------------------------------------------------------
//
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in
// the documentation and/or other materials provided with the distribution.
//
// 3. The names of the authors may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// -----------------------------------------------------------------------
//
// This program is based on zlib-1.1.3; credit to authors
// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
// and contributors of zlib.
//
// -----------------------------------------------------------------------
using System;
namespace BestHTTP.Decompression.Zlib
{
sealed class InfTree
{
private const int MANY = 1440;
private const int Z_OK = 0;
private const int Z_STREAM_END = 1;
private const int Z_NEED_DICT = 2;
private const int Z_ERRNO = - 1;
private const int Z_STREAM_ERROR = - 2;
private const int Z_DATA_ERROR = - 3;
private const int Z_MEM_ERROR = - 4;
private const int Z_BUF_ERROR = - 5;
private const int Z_VERSION_ERROR = - 6;
internal const int fixed_bl = 9;
internal const int fixed_bd = 5;
//UPGRADE_NOTE: Final was removed from the declaration of 'fixed_tl'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
internal static readonly int[] fixed_tl = new int[]{96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, 48, 0, 9, 192, 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 160, 0, 8, 0, 0, 8, 128, 0, 8, 64, 0, 9, 224, 80, 7, 6, 0, 8, 88, 0, 8, 24, 0, 9, 144, 83, 7, 59, 0, 8, 120, 0, 8, 56, 0, 9, 208, 81, 7, 17, 0, 8, 104, 0, 8, 40, 0, 9, 176, 0, 8, 8, 0, 8, 136, 0, 8, 72, 0, 9, 240, 80, 7, 4, 0, 8, 84, 0, 8, 20, 85, 8, 227, 83, 7, 43, 0, 8, 116, 0, 8, 52, 0, 9, 200, 81, 7, 13, 0, 8, 100, 0, 8, 36, 0, 9, 168, 0, 8, 4, 0, 8, 132, 0, 8, 68, 0, 9, 232, 80, 7, 8, 0, 8, 92, 0, 8, 28, 0, 9, 152, 84, 7, 83, 0, 8, 124, 0, 8, 60, 0, 9, 216, 82, 7, 23, 0, 8, 108, 0, 8, 44, 0, 9, 184, 0, 8, 12, 0, 8, 140, 0, 8, 76, 0, 9, 248, 80, 7, 3, 0, 8, 82, 0, 8, 18, 85, 8, 163, 83, 7, 35, 0, 8, 114, 0, 8, 50, 0, 9, 196, 81, 7, 11, 0, 8, 98, 0, 8, 34, 0, 9, 164, 0, 8, 2, 0, 8, 130, 0, 8, 66, 0, 9, 228, 80, 7, 7, 0, 8, 90, 0, 8, 26, 0, 9, 148, 84, 7, 67, 0, 8, 122, 0, 8, 58, 0, 9, 212, 82, 7, 19, 0, 8, 106, 0, 8, 42, 0, 9, 180, 0, 8, 10, 0, 8, 138, 0, 8, 74, 0, 9, 244, 80, 7, 5, 0, 8, 86, 0, 8, 22, 192, 8, 0, 83, 7, 51, 0, 8, 118, 0, 8, 54, 0, 9, 204, 81, 7, 15, 0, 8, 102, 0, 8, 38, 0, 9, 172, 0, 8, 6, 0, 8, 134, 0, 8, 70, 0, 9, 236, 80, 7, 9, 0, 8, 94, 0, 8, 30, 0, 9, 156, 84, 7, 99, 0, 8, 126, 0, 8, 62, 0, 9, 220, 82, 7, 27, 0, 8, 110, 0, 8, 46, 0, 9, 188, 0, 8, 14, 0, 8, 142, 0, 8, 78, 0, 9, 252, 96, 7, 256, 0, 8, 81, 0, 8, 17, 85, 8, 131, 82, 7, 31, 0, 8, 113, 0, 8, 49, 0, 9, 194, 80, 7, 10, 0, 8, 97, 0, 8, 33, 0, 9, 162, 0, 8, 1, 0, 8, 129, 0, 8, 65, 0, 9, 226, 80, 7, 6, 0, 8, 89, 0, 8, 25, 0, 9, 146, 83, 7, 59, 0, 8, 121, 0, 8, 57, 0, 9, 210, 81, 7, 17, 0, 8, 105, 0, 8, 41, 0, 9, 178, 0, 8, 9, 0, 8, 137, 0, 8, 73, 0, 9, 242, 80, 7, 4, 0, 8, 85, 0, 8, 21, 80, 8, 258, 83, 7, 43, 0, 8, 117, 0, 8, 53, 0, 9, 202, 81, 7, 13, 0, 8, 101, 0, 8, 37, 0, 9, 170, 0, 8, 5, 0, 8, 133, 0, 8, 69, 0, 9, 234, 80, 7, 8, 0, 8, 93, 0, 8, 29, 0, 9, 154, 84, 7, 83, 0, 8, 125, 0, 8, 61, 0, 9, 218, 82, 7, 23, 0, 8, 109, 0, 8, 45, 0, 9, 186,
0, 8, 13, 0, 8, 141, 0, 8, 77, 0, 9, 250, 80, 7, 3, 0, 8, 83, 0, 8, 19, 85, 8, 195, 83, 7, 35, 0, 8, 115, 0, 8, 51, 0, 9, 198, 81, 7, 11, 0, 8, 99, 0, 8, 35, 0, 9, 166, 0, 8, 3, 0, 8, 131, 0, 8, 67, 0, 9, 230, 80, 7, 7, 0, 8, 91, 0, 8, 27, 0, 9, 150, 84, 7, 67, 0, 8, 123, 0, 8, 59, 0, 9, 214, 82, 7, 19, 0, 8, 107, 0, 8, 43, 0, 9, 182, 0, 8, 11, 0, 8, 139, 0, 8, 75, 0, 9, 246, 80, 7, 5, 0, 8, 87, 0, 8, 23, 192, 8, 0, 83, 7, 51, 0, 8, 119, 0, 8, 55, 0, 9, 206, 81, 7, 15, 0, 8, 103, 0, 8, 39, 0, 9, 174, 0, 8, 7, 0, 8, 135, 0, 8, 71, 0, 9, 238, 80, 7, 9, 0, 8, 95, 0, 8, 31, 0, 9, 158, 84, 7, 99, 0, 8, 127, 0, 8, 63, 0, 9, 222, 82, 7, 27, 0, 8, 111, 0, 8, 47, 0, 9, 190, 0, 8, 15, 0, 8, 143, 0, 8, 79, 0, 9, 254, 96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, 48, 0, 9, 193, 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 161, 0, 8, 0, 0, 8, 128, 0, 8, 64, 0, 9, 225, 80, 7, 6, 0, 8, 88, 0, 8, 24, 0, 9, 145, 83, 7, 59, 0, 8, 120, 0, 8, 56, 0, 9, 209, 81, 7, 17, 0, 8, 104, 0, 8, 40, 0, 9, 177, 0, 8, 8, 0, 8, 136, 0, 8, 72, 0, 9, 241, 80, 7, 4, 0, 8, 84, 0, 8, 20, 85, 8, 227, 83, 7, 43, 0, 8, 116, 0, 8, 52, 0, 9, 201, 81, 7, 13, 0, 8, 100, 0, 8, 36, 0, 9, 169, 0, 8, 4, 0, 8, 132, 0, 8, 68, 0, 9, 233, 80, 7, 8, 0, 8, 92, 0, 8, 28, 0, 9, 153, 84, 7, 83, 0, 8, 124, 0, 8, 60, 0, 9, 217, 82, 7, 23, 0, 8, 108, 0, 8, 44, 0, 9, 185, 0, 8, 12, 0, 8, 140, 0, 8, 76, 0, 9, 249, 80, 7, 3, 0, 8, 82, 0, 8, 18, 85, 8, 163, 83, 7, 35, 0, 8, 114, 0, 8, 50, 0, 9, 197, 81, 7, 11, 0, 8, 98, 0, 8, 34, 0, 9, 165, 0, 8, 2, 0, 8, 130, 0, 8, 66, 0, 9, 229, 80, 7, 7, 0, 8, 90, 0, 8, 26, 0, 9, 149, 84, 7, 67, 0, 8, 122, 0, 8, 58, 0, 9, 213, 82, 7, 19, 0, 8, 106, 0, 8, 42, 0, 9, 181, 0, 8, 10, 0, 8, 138, 0, 8, 74, 0, 9, 245, 80, 7, 5, 0, 8, 86, 0, 8, 22, 192, 8, 0, 83, 7, 51, 0, 8, 118, 0, 8, 54, 0, 9, 205, 81, 7, 15, 0, 8, 102, 0, 8, 38, 0, 9, 173, 0, 8, 6, 0, 8, 134, 0, 8, 70, 0, 9, 237, 80, 7, 9, 0, 8, 94, 0, 8, 30, 0, 9, 157, 84, 7, 99, 0, 8, 126, 0, 8, 62, 0, 9, 221, 82, 7, 27, 0, 8, 110, 0, 8, 46, 0, 9, 189, 0, 8,
14, 0, 8, 142, 0, 8, 78, 0, 9, 253, 96, 7, 256, 0, 8, 81, 0, 8, 17, 85, 8, 131, 82, 7, 31, 0, 8, 113, 0, 8, 49, 0, 9, 195, 80, 7, 10, 0, 8, 97, 0, 8, 33, 0, 9, 163, 0, 8, 1, 0, 8, 129, 0, 8, 65, 0, 9, 227, 80, 7, 6, 0, 8, 89, 0, 8, 25, 0, 9, 147, 83, 7, 59, 0, 8, 121, 0, 8, 57, 0, 9, 211, 81, 7, 17, 0, 8, 105, 0, 8, 41, 0, 9, 179, 0, 8, 9, 0, 8, 137, 0, 8, 73, 0, 9, 243, 80, 7, 4, 0, 8, 85, 0, 8, 21, 80, 8, 258, 83, 7, 43, 0, 8, 117, 0, 8, 53, 0, 9, 203, 81, 7, 13, 0, 8, 101, 0, 8, 37, 0, 9, 171, 0, 8, 5, 0, 8, 133, 0, 8, 69, 0, 9, 235, 80, 7, 8, 0, 8, 93, 0, 8, 29, 0, 9, 155, 84, 7, 83, 0, 8, 125, 0, 8, 61, 0, 9, 219, 82, 7, 23, 0, 8, 109, 0, 8, 45, 0, 9, 187, 0, 8, 13, 0, 8, 141, 0, 8, 77, 0, 9, 251, 80, 7, 3, 0, 8, 83, 0, 8, 19, 85, 8, 195, 83, 7, 35, 0, 8, 115, 0, 8, 51, 0, 9, 199, 81, 7, 11, 0, 8, 99, 0, 8, 35, 0, 9, 167, 0, 8, 3, 0, 8, 131, 0, 8, 67, 0, 9, 231, 80, 7, 7, 0, 8, 91, 0, 8, 27, 0, 9, 151, 84, 7, 67, 0, 8, 123, 0, 8, 59, 0, 9, 215, 82, 7, 19, 0, 8, 107, 0, 8, 43, 0, 9, 183, 0, 8, 11, 0, 8, 139, 0, 8, 75, 0, 9, 247, 80, 7, 5, 0, 8, 87, 0, 8, 23, 192, 8, 0, 83, 7, 51, 0, 8, 119, 0, 8, 55, 0, 9, 207, 81, 7, 15, 0, 8, 103, 0, 8, 39, 0, 9, 175, 0, 8, 7, 0, 8, 135, 0, 8, 71, 0, 9, 239, 80, 7, 9, 0, 8, 95, 0, 8, 31, 0, 9, 159, 84, 7, 99, 0, 8, 127, 0, 8, 63, 0, 9, 223, 82, 7, 27, 0, 8, 111, 0, 8, 47, 0, 9, 191, 0, 8, 15, 0, 8, 143, 0, 8, 79, 0, 9, 255};
//UPGRADE_NOTE: Final was removed from the declaration of 'fixed_td'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
internal static readonly int[] fixed_td = new int[]{80, 5, 1, 87, 5, 257, 83, 5, 17, 91, 5, 4097, 81, 5, 5, 89, 5, 1025, 85, 5, 65, 93, 5, 16385, 80, 5, 3, 88, 5, 513, 84, 5, 33, 92, 5, 8193, 82, 5, 9, 90, 5, 2049, 86, 5, 129, 192, 5, 24577, 80, 5, 2, 87, 5, 385, 83, 5, 25, 91, 5, 6145, 81, 5, 7, 89, 5, 1537, 85, 5, 97, 93, 5, 24577, 80, 5, 4, 88, 5, 769, 84, 5, 49, 92, 5, 12289, 82, 5, 13, 90, 5, 3073, 86, 5, 193, 192, 5, 24577};
// Tables for deflate from PKZIP's appnote.txt.
//UPGRADE_NOTE: Final was removed from the declaration of 'cplens'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
internal static readonly int[] cplens = new int[]{3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
// see note #13 above about 258
//UPGRADE_NOTE: Final was removed from the declaration of 'cplext'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
internal static readonly int[] cplext = new int[]{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112};
//UPGRADE_NOTE: Final was removed from the declaration of 'cpdist'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
internal static readonly int[] cpdist = new int[]{1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577};
//UPGRADE_NOTE: Final was removed from the declaration of 'cpdext'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
internal static readonly int[] cpdext = new int[]{0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13};
// If BMAX needs to be larger than 16, then h and x[] should be uLong.
internal const int BMAX = 15; // maximum bit length of any code
internal int[] hn = null; // hufts used in space
internal int[] v = null; // work area for huft_build
internal int[] c = null; // bit length count table
internal int[] r = null; // table entity for structure assignment
internal int[] u = null; // table stack
internal int[] x = null; // bit offsets, then code stack
private int huft_build(int[] b, int bindex, int n, int s, int[] d, int[] e, int[] t, int[] m, int[] hp, int[] hn, int[] v)
{
// Given a list of code lengths and a maximum table size, make a set of
// tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR
// if the given code set is incomplete (the tables are still built in this
// case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of
// lengths), or Z_MEM_ERROR if not enough memory.
int a; // counter for codes of length k
int f; // i repeats in table every f entries
int g; // maximum code length
int h; // table level
int i; // counter, current code
int j; // counter
int k; // number of bits in current code
int l; // bits per table (returned in m)
int mask; // (1 << w) - 1, to avoid cc -O bug on HP
int p; // pointer into c[], b[], or v[]
int q; // points to current table
int w; // bits before this table == (l * h)
int xp; // pointer into x
int y; // number of dummy codes added
int z; // number of entries in current table
// Generate counts for each bit length
p = 0; i = n;
do
{
c[b[bindex + p]]++; p++; i--; // assume all entries <= BMAX
}
while (i != 0);
if (c[0] == n)
{
// null input--all zero length codes
t[0] = - 1;
m[0] = 0;
return Z_OK;
}
// Find minimum and maximum length, bound *m by those
l = m[0];
for (j = 1; j <= BMAX; j++)
if (c[j] != 0)
break;
k = j; // minimum code length
if (l < j)
{
l = j;
}
for (i = BMAX; i != 0; i--)
{
if (c[i] != 0)
break;
}
g = i; // maximum code length
if (l > i)
{
l = i;
}
m[0] = l;
// Adjust last length count to fill out codes, if needed
for (y = 1 << j; j < i; j++, y <<= 1)
{
if ((y -= c[j]) < 0)
{
return Z_DATA_ERROR;
}
}
if ((y -= c[i]) < 0)
{
return Z_DATA_ERROR;
}
c[i] += y;
// Generate starting offsets into the value table for each length
x[1] = j = 0;
p = 1; xp = 2;
while (--i != 0)
{
// note that i == g from above
x[xp] = (j += c[p]);
xp++;
p++;
}
// Make a table of values in order of bit lengths
i = 0; p = 0;
do
{
if ((j = b[bindex + p]) != 0)
{
v[x[j]++] = i;
}
p++;
}
while (++i < n);
n = x[g]; // set n to length of v
// Generate the Huffman codes and for each, make the table entries
x[0] = i = 0; // first Huffman code is zero
p = 0; // grab values in bit order
h = - 1; // no tables yet--level -1
w = - l; // bits decoded == (l * h)
u[0] = 0; // just to keep compilers happy
q = 0; // ditto
z = 0; // ditto
// go through the bit lengths (k already is bits in shortest code)
for (; k <= g; k++)
{
a = c[k];
while (a-- != 0)
{
// here i is the Huffman code of length k bits for value *p
// make tables up to required level
while (k > w + l)
{
h++;
w += l; // previous table always l bits
// compute minimum size table less than or equal to l bits
z = g - w;
z = (z > l)?l:z; // table size upper limit
if ((f = 1 << (j = k - w)) > a + 1)
{
// try a k-w bit table
// too few codes for k-w bit table
f -= (a + 1); // deduct codes from patterns left
xp = k;
if (j < z)
{
while (++j < z)
{
// try smaller tables up to z bits
if ((f <<= 1) <= c[++xp])
break; // enough codes to use up j bits
f -= c[xp]; // else deduct codes from patterns
}
}
}
z = 1 << j; // table entries for j-bit table
// allocate new table
if (hn[0] + z > MANY)
{
// (note: doesn't matter for fixed)
return Z_DATA_ERROR; // overflow of MANY
}
u[h] = q = hn[0]; // DEBUG
hn[0] += z;
// connect to last table, if there is one
if (h != 0)
{
x[h] = i; // save pattern for backing up
r[0] = (sbyte) j; // bits in this table
r[1] = (sbyte) l; // bits to dump before this table
j = SharedUtils.URShift(i, (w - l));
r[2] = (int) (q - u[h - 1] - j); // offset to this table
Array.Copy(r, 0, hp, (u[h - 1] + j) * 3, 3); // connect to last table
}
else
{
t[0] = q; // first table is returned result
}
}
// set up table entity in r
r[1] = (sbyte) (k - w);
if (p >= n)
{
r[0] = 128 + 64; // out of values--invalid code
}
else if (v[p] < s)
{
r[0] = (sbyte) (v[p] < 256?0:32 + 64); // 256 is end-of-block
r[2] = v[p++]; // simple code is just the value
}
else
{
r[0] = (sbyte) (e[v[p] - s] + 16 + 64); // non-simple--look up in lists
r[2] = d[v[p++] - s];
}
// fill code-like entries with r
f = 1 << (k - w);
for (j = SharedUtils.URShift(i, w); j < z; j += f)
{
Array.Copy(r, 0, hp, (q + j) * 3, 3);
}
// backwards increment the k-bit code i
for (j = 1 << (k - 1); (i & j) != 0; j = SharedUtils.URShift(j, 1))
{
i ^= j;
}
i ^= j;
// backup over finished tables
mask = (1 << w) - 1; // needed on HP, cc -O bug
while ((i & mask) != x[h])
{
h--; // don't need to update q
w -= l;
mask = (1 << w) - 1;
}
}
}
// Return Z_BUF_ERROR if we were given an incomplete table
return y != 0 && g != 1?Z_BUF_ERROR:Z_OK;
}
internal int inflate_trees_bits(int[] c, int[] bb, int[] tb, int[] hp, ZlibCodec z)
{
int result;
initWorkArea(19);
hn[0] = 0;
result = huft_build(c, 0, 19, 19, null, null, tb, bb, hp, hn, v);
if (result == Z_DATA_ERROR)
{
z.Message = "oversubscribed dynamic bit lengths tree";
}
else if (result == Z_BUF_ERROR || bb[0] == 0)
{
z.Message = "incomplete dynamic bit lengths tree";
result = Z_DATA_ERROR;
}
return result;
}
internal int inflate_trees_dynamic(int nl, int nd, int[] c, int[] bl, int[] bd, int[] tl, int[] td, int[] hp, ZlibCodec z)
{
int result;
// build literal/length tree
initWorkArea(288);
hn[0] = 0;
result = huft_build(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v);
if (result != Z_OK || bl[0] == 0)
{
if (result == Z_DATA_ERROR)
{
z.Message = "oversubscribed literal/length tree";
}
else if (result != Z_MEM_ERROR)
{
z.Message = "incomplete literal/length tree";
result = Z_DATA_ERROR;
}
return result;
}
// build distance tree
initWorkArea(288);
result = huft_build(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v);
if (result != Z_OK || (bd[0] == 0 && nl > 257))
{
if (result == Z_DATA_ERROR)
{
z.Message = "oversubscribed distance tree";
}
else if (result == Z_BUF_ERROR)
{
z.Message = "incomplete distance tree";
result = Z_DATA_ERROR;
}
else if (result != Z_MEM_ERROR)
{
z.Message = "empty distance tree with lengths";
result = Z_DATA_ERROR;
}
return result;
}
return Z_OK;
}
internal static int inflate_trees_fixed(int[] bl, int[] bd, int[][] tl, int[][] td, ZlibCodec z)
{
bl[0] = fixed_bl;
bd[0] = fixed_bd;
tl[0] = fixed_tl;
td[0] = fixed_td;
return Z_OK;
}
private void initWorkArea(int vsize)
{
if (hn == null)
{
hn = new int[1];
v = new int[vsize];
c = new int[BMAX + 1];
r = new int[3];
u = new int[BMAX];
x = new int[BMAX + 1];
}
else
{
if (v.Length < vsize)
{
v = new int[vsize];
}
Array.Clear(v,0,vsize);
Array.Clear(c,0,BMAX+1);
r[0]=0; r[1]=0; r[2]=0;
// for(int i=0; i<BMAX; i++){u[i]=0;}
//Array.Copy(c, 0, u, 0, BMAX);
Array.Clear(u,0,BMAX);
// for(int i=0; i<BMAX+1; i++){x[i]=0;}
//Array.Copy(c, 0, x, 0, BMAX + 1);
Array.Clear(x,0,BMAX+1);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b87bcccbfddd540179bbf9b457c7de30
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4dcdc33d14fa441f59ee54922afed019
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,423 @@
// Tree.cs
// ------------------------------------------------------------------
//
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
// All rights reserved.
//
// This code module is part of DotNetZip, a zipfile class library.
//
// ------------------------------------------------------------------
//
// This code is licensed under the Microsoft Public License.
// See the file License.txt for the license details.
// More info on: http://dotnetzip.codeplex.com
//
// ------------------------------------------------------------------
//
// last saved (in emacs):
// Time-stamp: <2009-October-28 13:29:50>
//
// ------------------------------------------------------------------
//
// This module defines classes for zlib compression and
// decompression. This code is derived from the jzlib implementation of
// zlib. In keeping with the license for jzlib, the copyright to that
// code is below.
//
// ------------------------------------------------------------------
//
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in
// the documentation and/or other materials provided with the distribution.
//
// 3. The names of the authors may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// -----------------------------------------------------------------------
//
// This program is based on zlib-1.1.3; credit to authors
// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
// and contributors of zlib.
//
// -----------------------------------------------------------------------
using System;
namespace BestHTTP.Decompression.Zlib
{
sealed class ZTree
{
private static readonly int HEAP_SIZE = (2 * InternalConstants.L_CODES + 1);
// extra bits for each length code
internal static readonly int[] ExtraLengthBits = new int[]
{
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
};
// extra bits for each distance code
internal static readonly int[] ExtraDistanceBits = new int[]
{
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13
};
// extra bits for each bit length code
internal static readonly int[] extra_blbits = new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7};
internal static readonly sbyte[] bl_order = new sbyte[]{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
// The lengths of the bit length codes are sent in order of decreasing
// probability, to avoid transmitting the lengths for unused bit
// length codes.
internal const int Buf_size = 8 * 2;
// see definition of array dist_code below
//internal const int DIST_CODE_LEN = 512;
private static readonly sbyte[] _dist_code = new sbyte[]
{
0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7,
8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
0, 0, 16, 17, 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21,
22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
};
internal static readonly sbyte[] LengthCode = new sbyte[]
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11,
12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15,
16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17,
18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28
};
internal static readonly int[] LengthBase = new int[]
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28,
32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 0
};
internal static readonly int[] DistanceBase = new int[]
{
0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192,
256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576
};
/// <summary>
/// Map from a distance to a distance code.
/// </summary>
/// <remarks>
/// No side effects. _dist_code[256] and _dist_code[257] are never used.
/// </remarks>
internal static int DistanceCode(int dist)
{
return (dist < 256)
? _dist_code[dist]
: _dist_code[256 + SharedUtils.URShift(dist, 7)];
}
internal short[] dyn_tree; // the dynamic tree
internal int max_code; // largest code with non zero frequency
internal StaticTree staticTree; // the corresponding static tree
// Compute the optimal bit lengths for a tree and update the total bit length
// for the current block.
// IN assertion: the fields freq and dad are set, heap[heap_max] and
// above are the tree nodes sorted by increasing frequency.
// OUT assertions: the field len is set to the optimal bit length, the
// array bl_count contains the frequencies for each bit length.
// The length opt_len is updated; static_len is also updated if stree is
// not null.
internal void gen_bitlen(DeflateManager s)
{
short[] tree = dyn_tree;
short[] stree = staticTree.treeCodes;
int[] extra = staticTree.extraBits;
int base_Renamed = staticTree.extraBase;
int max_length = staticTree.maxLength;
int h; // heap index
int n, m; // iterate over the tree elements
int bits; // bit length
int xbits; // extra bits
short f; // frequency
int overflow = 0; // number of elements with bit length too large
for (bits = 0; bits <= InternalConstants.MAX_BITS; bits++)
s.bl_count[bits] = 0;
// In a first pass, compute the optimal bit lengths (which may
// overflow in the case of the bit length tree).
tree[s.heap[s.heap_max] * 2 + 1] = 0; // root of the heap
for (h = s.heap_max + 1; h < HEAP_SIZE; h++)
{
n = s.heap[h];
bits = tree[tree[n * 2 + 1] * 2 + 1] + 1;
if (bits > max_length)
{
bits = max_length; overflow++;
}
tree[n * 2 + 1] = (short) bits;
// We overwrite tree[n*2+1] which is no longer needed
if (n > max_code)
continue; // not a leaf node
s.bl_count[bits]++;
xbits = 0;
if (n >= base_Renamed)
xbits = extra[n - base_Renamed];
f = tree[n * 2];
s.opt_len += f * (bits + xbits);
if (stree != null)
s.static_len += f * (stree[n * 2 + 1] + xbits);
}
if (overflow == 0)
return ;
// This happens for example on obj2 and pic of the Calgary corpus
// Find the first bit length which could increase:
do
{
bits = max_length - 1;
while (s.bl_count[bits] == 0)
bits--;
s.bl_count[bits]--; // move one leaf down the tree
s.bl_count[bits + 1] = (short) (s.bl_count[bits + 1] + 2); // move one overflow item as its brother
s.bl_count[max_length]--;
// The brother of the overflow item also moves one step up,
// but this does not affect bl_count[max_length]
overflow -= 2;
}
while (overflow > 0);
for (bits = max_length; bits != 0; bits--)
{
n = s.bl_count[bits];
while (n != 0)
{
m = s.heap[--h];
if (m > max_code)
continue;
if (tree[m * 2 + 1] != bits)
{
s.opt_len = (int) (s.opt_len + ((long) bits - (long) tree[m * 2 + 1]) * (long) tree[m * 2]);
tree[m * 2 + 1] = (short) bits;
}
n--;
}
}
}
// Construct one Huffman tree and assigns the code bit strings and lengths.
// Update the total bit length for the current block.
// IN assertion: the field freq is set for all tree elements.
// OUT assertions: the fields len and code are set to the optimal bit length
// and corresponding code. The length opt_len is updated; static_len is
// also updated if stree is not null. The field max_code is set.
internal void build_tree(DeflateManager s)
{
short[] tree = dyn_tree;
short[] stree = staticTree.treeCodes;
int elems = staticTree.elems;
int n, m; // iterate over heap elements
int max_code = -1; // largest code with non zero frequency
int node; // new node being created
// Construct the initial heap, with least frequent element in
// heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
// heap[0] is not used.
s.heap_len = 0;
s.heap_max = HEAP_SIZE;
for (n = 0; n < elems; n++)
{
if (tree[n * 2] != 0)
{
s.heap[++s.heap_len] = max_code = n;
s.depth[n] = 0;
}
else
{
tree[n * 2 + 1] = 0;
}
}
// The pkzip format requires that at least one distance code exists,
// and that at least one bit should be sent even if there is only one
// possible code. So to avoid special checks later on we force at least
// two codes of non zero frequency.
while (s.heap_len < 2)
{
node = s.heap[++s.heap_len] = (max_code < 2?++max_code:0);
tree[node * 2] = 1;
s.depth[node] = 0;
s.opt_len--;
if (stree != null)
s.static_len -= stree[node * 2 + 1];
// node is 0 or 1 so it does not have extra bits
}
this.max_code = max_code;
// The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
// establish sub-heaps of increasing lengths:
for (n = s.heap_len / 2; n >= 1; n--)
s.pqdownheap(tree, n);
// Construct the Huffman tree by repeatedly combining the least two
// frequent nodes.
node = elems; // next internal node of the tree
do
{
// n = node of least frequency
n = s.heap[1];
s.heap[1] = s.heap[s.heap_len--];
s.pqdownheap(tree, 1);
m = s.heap[1]; // m = node of next least frequency
s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency
s.heap[--s.heap_max] = m;
// Create a new node father of n and m
tree[node * 2] = unchecked((short) (tree[n * 2] + tree[m * 2]));
s.depth[node] = (sbyte) (System.Math.Max((byte) s.depth[n], (byte) s.depth[m]) + 1);
tree[n * 2 + 1] = tree[m * 2 + 1] = (short) node;
// and insert the new node in the heap
s.heap[1] = node++;
s.pqdownheap(tree, 1);
}
while (s.heap_len >= 2);
s.heap[--s.heap_max] = s.heap[1];
// At this point, the fields freq and dad are set. We can now
// generate the bit lengths.
gen_bitlen(s);
// The field len is now set, we can generate the bit codes
gen_codes(tree, max_code, s.bl_count);
}
// Generate the codes for a given tree and bit counts (which need not be
// optimal).
// IN assertion: the array bl_count contains the bit length statistics for
// the given tree and the field len is set for all tree elements.
// OUT assertion: the field code is set for all tree elements of non
// zero code length.
internal static void gen_codes(short[] tree, int max_code, short[] bl_count)
{
short[] next_code = new short[InternalConstants.MAX_BITS + 1]; // next code value for each bit length
short code = 0; // running code value
int bits; // bit index
int n; // code index
// The distribution counts are first used to generate the code values
// without bit reversal.
for (bits = 1; bits <= InternalConstants.MAX_BITS; bits++)
unchecked {
next_code[bits] = code = (short) ((code + bl_count[bits - 1]) << 1);
}
// Check that the bit counts in bl_count are consistent. The last code
// must be all ones.
//Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
// "inconsistent bit counts");
//Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
for (n = 0; n <= max_code; n++)
{
int len = tree[n * 2 + 1];
if (len == 0)
continue;
// Now reverse the bits
tree[n * 2] = unchecked((short) (bi_reverse(next_code[len]++, len)));
}
}
// Reverse the first len bits of a code, using straightforward code (a faster
// method would use a table)
// IN assertion: 1 <= len <= 15
internal static int bi_reverse(int code, int len)
{
int res = 0;
do
{
res |= code & 1;
code >>= 1; //SharedUtils.URShift(code, 1);
res <<= 1;
}
while (--len > 0);
return res >> 1;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 43cc5df3b150c4ac29fe4c49e9b4dc2a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,546 @@
// Zlib.cs
// ------------------------------------------------------------------
//
// Copyright (c) 2009-2011 Dino Chiesa and Microsoft Corporation.
// All rights reserved.
//
// This code module is part of DotNetZip, a zipfile class library.
//
// ------------------------------------------------------------------
//
// This code is licensed under the Microsoft Public License.
// See the file License.txt for the license details.
// More info on: http://dotnetzip.codeplex.com
//
// ------------------------------------------------------------------
//
// Last Saved: <2011-August-03 19:52:28>
//
// ------------------------------------------------------------------
//
// This module defines classes for ZLIB compression and
// decompression. This code is derived from the jzlib implementation of
// zlib, but significantly modified. The object model is not the same,
// and many of the behaviors are new or different. Nonetheless, in
// keeping with the license for jzlib, the copyright to that code is
// included below.
//
// ------------------------------------------------------------------
//
// The following notice applies to jzlib:
//
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in
// the documentation and/or other materials provided with the distribution.
//
// 3. The names of the authors may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// -----------------------------------------------------------------------
//
// jzlib is based on zlib-1.1.3.
//
// The following notice applies to zlib:
//
// -----------------------------------------------------------------------
//
// Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler
//
// The ZLIB software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
// Jean-loup Gailly jloup@gzip.org
// Mark Adler madler@alumni.caltech.edu
//
// -----------------------------------------------------------------------
using System;
using Interop=System.Runtime.InteropServices;
namespace BestHTTP.Decompression.Zlib
{
/// <summary>
/// Describes how to flush the current deflate operation.
/// </summary>
/// <remarks>
/// The different FlushType values are useful when using a Deflate in a streaming application.
/// </remarks>
public enum FlushType
{
/// <summary>No flush at all.</summary>
None = 0,
/// <summary>Closes the current block, but doesn't flush it to
/// the output. Used internally only in hypothetical
/// scenarios. This was supposed to be removed by Zlib, but it is
/// still in use in some edge cases.
/// </summary>
Partial,
/// <summary>
/// Use this during compression to specify that all pending output should be
/// flushed to the output buffer and the output should be aligned on a byte
/// boundary. You might use this in a streaming communication scenario, so that
/// the decompressor can get all input data available so far. When using this
/// with a ZlibCodec, <c>AvailableBytesIn</c> will be zero after the call if
/// enough output space has been provided before the call. Flushing will
/// degrade compression and so it should be used only when necessary.
/// </summary>
Sync,
/// <summary>
/// Use this during compression to specify that all output should be flushed, as
/// with <c>FlushType.Sync</c>, but also, the compression state should be reset
/// so that decompression can restart from this point if previous compressed
/// data has been damaged or if random access is desired. Using
/// <c>FlushType.Full</c> too often can significantly degrade the compression.
/// </summary>
Full,
/// <summary>Signals the end of the compression/decompression stream.</summary>
Finish,
}
/// <summary>
/// The compression level to be used when using a DeflateStream or ZlibStream with CompressionMode.Compress.
/// </summary>
public enum CompressionLevel
{
/// <summary>
/// None means that the data will be simply stored, with no change at all.
/// If you are producing ZIPs for use on Mac OSX, be aware that archives produced with CompressionLevel.None
/// cannot be opened with the default zip reader. Use a different CompressionLevel.
/// </summary>
None= 0,
/// <summary>
/// Same as None.
/// </summary>
Level0 = 0,
/// <summary>
/// The fastest but least effective compression.
/// </summary>
BestSpeed = 1,
/// <summary>
/// A synonym for BestSpeed.
/// </summary>
Level1 = 1,
/// <summary>
/// A little slower, but better, than level 1.
/// </summary>
Level2 = 2,
/// <summary>
/// A little slower, but better, than level 2.
/// </summary>
Level3 = 3,
/// <summary>
/// A little slower, but better, than level 3.
/// </summary>
Level4 = 4,
/// <summary>
/// A little slower than level 4, but with better compression.
/// </summary>
Level5 = 5,
/// <summary>
/// The default compression level, with a good balance of speed and compression efficiency.
/// </summary>
Default = 6,
/// <summary>
/// A synonym for Default.
/// </summary>
Level6 = 6,
/// <summary>
/// Pretty good compression!
/// </summary>
Level7 = 7,
/// <summary>
/// Better compression than Level7!
/// </summary>
Level8 = 8,
/// <summary>
/// The "best" compression, where best means greatest reduction in size of the input data stream.
/// This is also the slowest compression.
/// </summary>
BestCompression = 9,
/// <summary>
/// A synonym for BestCompression.
/// </summary>
Level9 = 9,
}
/// <summary>
/// Describes options for how the compression algorithm is executed. Different strategies
/// work better on different sorts of data. The strategy parameter can affect the compression
/// ratio and the speed of compression but not the correctness of the compresssion.
/// </summary>
public enum CompressionStrategy
{
/// <summary>
/// The default strategy is probably the best for normal data.
/// </summary>
Default = 0,
/// <summary>
/// The <c>Filtered</c> strategy is intended to be used most effectively with data produced by a
/// filter or predictor. By this definition, filtered data consists mostly of small
/// values with a somewhat random distribution. In this case, the compression algorithm
/// is tuned to compress them better. The effect of <c>Filtered</c> is to force more Huffman
/// coding and less string matching; it is a half-step between <c>Default</c> and <c>HuffmanOnly</c>.
/// </summary>
Filtered = 1,
/// <summary>
/// Using <c>HuffmanOnly</c> will force the compressor to do Huffman encoding only, with no
/// string matching.
/// </summary>
HuffmanOnly = 2,
}
/// <summary>
/// An enum to specify the direction of transcoding - whether to compress or decompress.
/// </summary>
public enum CompressionMode
{
/// <summary>
/// Used to specify that the stream should compress the data.
/// </summary>
Compress= 0,
/// <summary>
/// Used to specify that the stream should decompress the data.
/// </summary>
Decompress = 1,
}
/// <summary>
/// A general purpose exception class for exceptions in the Zlib library.
/// </summary>
[Interop.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d0000E")]
internal class ZlibException : System.Exception
{
/// <summary>
/// The ZlibException class captures exception information generated
/// by the Zlib library.
/// </summary>
public ZlibException()
: base()
{
}
/// <summary>
/// This ctor collects a message attached to the exception.
/// </summary>
/// <param name="s">the message for the exception.</param>
public ZlibException(System.String s)
: base(s)
{
}
}
internal class SharedUtils
{
/// <summary>
/// Performs an unsigned bitwise right shift with the specified number
/// </summary>
/// <param name="number">Number to operate on</param>
/// <param name="bits">Ammount of bits to shift</param>
/// <returns>The resulting number from the shift operation</returns>
public static int URShift(int number, int bits)
{
return (int)((uint)number >> bits);
}
#if NOT
/// <summary>
/// Performs an unsigned bitwise right shift with the specified number
/// </summary>
/// <param name="number">Number to operate on</param>
/// <param name="bits">Ammount of bits to shift</param>
/// <returns>The resulting number from the shift operation</returns>
public static long URShift(long number, int bits)
{
return (long) ((UInt64)number >> bits);
}
#endif
/// <summary>
/// Reads a number of characters from the current source TextReader and writes
/// the data to the target array at the specified index.
/// </summary>
///
/// <param name="sourceTextReader">The source TextReader to read from</param>
/// <param name="target">Contains the array of characteres read from the source TextReader.</param>
/// <param name="start">The starting index of the target array.</param>
/// <param name="count">The maximum number of characters to read from the source TextReader.</param>
///
/// <returns>
/// The number of characters read. The number will be less than or equal to
/// count depending on the data available in the source TextReader. Returns -1
/// if the end of the stream is reached.
/// </returns>
public static System.Int32 ReadInput(System.IO.TextReader sourceTextReader, byte[] target, int start, int count)
{
// Returns 0 bytes if not enough space in target
if (target.Length == 0) return 0;
char[] charArray = new char[target.Length];
int bytesRead = sourceTextReader.Read(charArray, start, count);
// Returns -1 if EOF
if (bytesRead == 0) return -1;
for (int index = start; index < start + bytesRead; index++)
target[index] = (byte)charArray[index];
return bytesRead;
}
internal static byte[] ToByteArray(System.String sourceString)
{
return System.Text.UTF8Encoding.UTF8.GetBytes(sourceString);
}
internal static char[] ToCharArray(byte[] byteArray)
{
return System.Text.UTF8Encoding.UTF8.GetChars(byteArray);
}
}
internal static class InternalConstants
{
internal static readonly int MAX_BITS = 15;
internal static readonly int BL_CODES = 19;
internal static readonly int D_CODES = 30;
internal static readonly int LITERALS = 256;
internal static readonly int LENGTH_CODES = 29;
internal static readonly int L_CODES = (LITERALS + 1 + LENGTH_CODES);
// Bit length codes must not exceed MAX_BL_BITS bits
internal static readonly int MAX_BL_BITS = 7;
// repeat previous bit length 3-6 times (2 bits of repeat count)
internal static readonly int REP_3_6 = 16;
// repeat a zero length 3-10 times (3 bits of repeat count)
internal static readonly int REPZ_3_10 = 17;
// repeat a zero length 11-138 times (7 bits of repeat count)
internal static readonly int REPZ_11_138 = 18;
}
internal sealed class StaticTree
{
internal static readonly short[] lengthAndLiteralsTreeCodes = new short[] {
12, 8, 140, 8, 76, 8, 204, 8, 44, 8, 172, 8, 108, 8, 236, 8,
28, 8, 156, 8, 92, 8, 220, 8, 60, 8, 188, 8, 124, 8, 252, 8,
2, 8, 130, 8, 66, 8, 194, 8, 34, 8, 162, 8, 98, 8, 226, 8,
18, 8, 146, 8, 82, 8, 210, 8, 50, 8, 178, 8, 114, 8, 242, 8,
10, 8, 138, 8, 74, 8, 202, 8, 42, 8, 170, 8, 106, 8, 234, 8,
26, 8, 154, 8, 90, 8, 218, 8, 58, 8, 186, 8, 122, 8, 250, 8,
6, 8, 134, 8, 70, 8, 198, 8, 38, 8, 166, 8, 102, 8, 230, 8,
22, 8, 150, 8, 86, 8, 214, 8, 54, 8, 182, 8, 118, 8, 246, 8,
14, 8, 142, 8, 78, 8, 206, 8, 46, 8, 174, 8, 110, 8, 238, 8,
30, 8, 158, 8, 94, 8, 222, 8, 62, 8, 190, 8, 126, 8, 254, 8,
1, 8, 129, 8, 65, 8, 193, 8, 33, 8, 161, 8, 97, 8, 225, 8,
17, 8, 145, 8, 81, 8, 209, 8, 49, 8, 177, 8, 113, 8, 241, 8,
9, 8, 137, 8, 73, 8, 201, 8, 41, 8, 169, 8, 105, 8, 233, 8,
25, 8, 153, 8, 89, 8, 217, 8, 57, 8, 185, 8, 121, 8, 249, 8,
5, 8, 133, 8, 69, 8, 197, 8, 37, 8, 165, 8, 101, 8, 229, 8,
21, 8, 149, 8, 85, 8, 213, 8, 53, 8, 181, 8, 117, 8, 245, 8,
13, 8, 141, 8, 77, 8, 205, 8, 45, 8, 173, 8, 109, 8, 237, 8,
29, 8, 157, 8, 93, 8, 221, 8, 61, 8, 189, 8, 125, 8, 253, 8,
19, 9, 275, 9, 147, 9, 403, 9, 83, 9, 339, 9, 211, 9, 467, 9,
51, 9, 307, 9, 179, 9, 435, 9, 115, 9, 371, 9, 243, 9, 499, 9,
11, 9, 267, 9, 139, 9, 395, 9, 75, 9, 331, 9, 203, 9, 459, 9,
43, 9, 299, 9, 171, 9, 427, 9, 107, 9, 363, 9, 235, 9, 491, 9,
27, 9, 283, 9, 155, 9, 411, 9, 91, 9, 347, 9, 219, 9, 475, 9,
59, 9, 315, 9, 187, 9, 443, 9, 123, 9, 379, 9, 251, 9, 507, 9,
7, 9, 263, 9, 135, 9, 391, 9, 71, 9, 327, 9, 199, 9, 455, 9,
39, 9, 295, 9, 167, 9, 423, 9, 103, 9, 359, 9, 231, 9, 487, 9,
23, 9, 279, 9, 151, 9, 407, 9, 87, 9, 343, 9, 215, 9, 471, 9,
55, 9, 311, 9, 183, 9, 439, 9, 119, 9, 375, 9, 247, 9, 503, 9,
15, 9, 271, 9, 143, 9, 399, 9, 79, 9, 335, 9, 207, 9, 463, 9,
47, 9, 303, 9, 175, 9, 431, 9, 111, 9, 367, 9, 239, 9, 495, 9,
31, 9, 287, 9, 159, 9, 415, 9, 95, 9, 351, 9, 223, 9, 479, 9,
63, 9, 319, 9, 191, 9, 447, 9, 127, 9, 383, 9, 255, 9, 511, 9,
0, 7, 64, 7, 32, 7, 96, 7, 16, 7, 80, 7, 48, 7, 112, 7,
8, 7, 72, 7, 40, 7, 104, 7, 24, 7, 88, 7, 56, 7, 120, 7,
4, 7, 68, 7, 36, 7, 100, 7, 20, 7, 84, 7, 52, 7, 116, 7,
3, 8, 131, 8, 67, 8, 195, 8, 35, 8, 163, 8, 99, 8, 227, 8
};
internal static readonly short[] distTreeCodes = new short[] {
0, 5, 16, 5, 8, 5, 24, 5, 4, 5, 20, 5, 12, 5, 28, 5,
2, 5, 18, 5, 10, 5, 26, 5, 6, 5, 22, 5, 14, 5, 30, 5,
1, 5, 17, 5, 9, 5, 25, 5, 5, 5, 21, 5, 13, 5, 29, 5,
3, 5, 19, 5, 11, 5, 27, 5, 7, 5, 23, 5 };
internal static readonly StaticTree Literals;
internal static readonly StaticTree Distances;
internal static readonly StaticTree BitLengths;
internal short[] treeCodes; // static tree or null
internal int[] extraBits; // extra bits for each code or null
internal int extraBase; // base index for extra_bits
internal int elems; // max number of elements in the tree
internal int maxLength; // max bit length for the codes
private StaticTree(short[] treeCodes, int[] extraBits, int extraBase, int elems, int maxLength)
{
this.treeCodes = treeCodes;
this.extraBits = extraBits;
this.extraBase = extraBase;
this.elems = elems;
this.maxLength = maxLength;
}
static StaticTree()
{
Literals = new StaticTree(lengthAndLiteralsTreeCodes, ZTree.ExtraLengthBits, InternalConstants.LITERALS + 1, InternalConstants.L_CODES, InternalConstants.MAX_BITS);
Distances = new StaticTree(distTreeCodes, ZTree.ExtraDistanceBits, 0, InternalConstants.D_CODES, InternalConstants.MAX_BITS);
BitLengths = new StaticTree(null, ZTree.extra_blbits, 0, InternalConstants.BL_CODES, InternalConstants.MAX_BL_BITS);
}
}
/// <summary>
/// Computes an Adler-32 checksum.
/// </summary>
/// <remarks>
/// The Adler checksum is similar to a CRC checksum, but faster to compute, though less
/// reliable. It is used in producing RFC1950 compressed streams. The Adler checksum
/// is a required part of the "ZLIB" standard. Applications will almost never need to
/// use this class directly.
/// </remarks>
///
/// <exclude/>
public sealed class Adler
{
// largest prime smaller than 65536
private static readonly uint BASE = 65521;
// NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1
private static readonly int NMAX = 5552;
#pragma warning disable 3001
#pragma warning disable 3002
/// <summary>
/// Calculates the Adler32 checksum.
/// </summary>
/// <remarks>
/// <para>
/// This is used within ZLIB. You probably don't need to use this directly.
/// </para>
/// </remarks>
/// <example>
/// To compute an Adler32 checksum on a byte array:
/// <code>
/// var adler = Adler.Adler32(0, null, 0, 0);
/// adler = Adler.Adler32(adler, buffer, index, length);
/// </code>
/// </example>
public static uint Adler32(uint adler, byte[] buf, int index, int len)
{
if (buf == null)
return 1;
uint s1 = (uint) (adler & 0xffff);
uint s2 = (uint) ((adler >> 16) & 0xffff);
while (len > 0)
{
int k = len < NMAX ? len : NMAX;
len -= k;
while (k >= 16)
{
//s1 += (buf[index++] & 0xff); s2 += s1;
s1 += buf[index++]; s2 += s1;
s1 += buf[index++]; s2 += s1;
s1 += buf[index++]; s2 += s1;
s1 += buf[index++]; s2 += s1;
s1 += buf[index++]; s2 += s1;
s1 += buf[index++]; s2 += s1;
s1 += buf[index++]; s2 += s1;
s1 += buf[index++]; s2 += s1;
s1 += buf[index++]; s2 += s1;
s1 += buf[index++]; s2 += s1;
s1 += buf[index++]; s2 += s1;
s1 += buf[index++]; s2 += s1;
s1 += buf[index++]; s2 += s1;
s1 += buf[index++]; s2 += s1;
s1 += buf[index++]; s2 += s1;
s1 += buf[index++]; s2 += s1;
k -= 16;
}
if (k != 0)
{
do
{
s1 += buf[index++];
s2 += s1;
}
while (--k != 0);
}
s1 %= BASE;
s2 %= BASE;
}
return (uint)((s2 << 16) | s1);
}
#pragma warning restore 3001
#pragma warning restore 3002
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7848c70585579442ab0411bedc5277d5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,643 @@
// ZlibBaseStream.cs
// ------------------------------------------------------------------
//
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
// All rights reserved.
//
// This code module is part of DotNetZip, a zipfile class library.
//
// ------------------------------------------------------------------
//
// This code is licensed under the Microsoft Public License.
// See the file License.txt for the license details.
// More info on: http://dotnetzip.codeplex.com
//
// ------------------------------------------------------------------
//
// last saved (in emacs):
// Time-stamp: <2011-August-06 21:22:38>
//
// ------------------------------------------------------------------
//
// This module defines the ZlibBaseStream class, which is an intnernal
// base class for DeflateStream, ZlibStream and GZipStream.
//
// ------------------------------------------------------------------
using System;
using System.IO;
namespace BestHTTP.Decompression.Zlib
{
internal enum ZlibStreamFlavor { ZLIB = 1950, DEFLATE = 1951, GZIP = 1952 }
internal class ZlibBaseStream : System.IO.Stream
{
protected internal ZlibCodec _z = null; // deferred init... new ZlibCodec();
protected internal StreamMode _streamMode = StreamMode.Undefined;
protected internal FlushType _flushMode;
protected internal ZlibStreamFlavor _flavor;
protected internal CompressionMode _compressionMode;
protected internal CompressionLevel _level;
protected internal bool _leaveOpen;
protected internal byte[] _workingBuffer;
protected internal int _bufferSize = ZlibConstants.WorkingBufferSizeDefault;
protected internal int windowBitsMax;
protected internal byte[] _buf1 = new byte[1];
protected internal System.IO.Stream _stream;
protected internal CompressionStrategy Strategy = CompressionStrategy.Default;
// workitem 7159
BestHTTP.Decompression.Crc.CRC32 crc;
protected internal string _GzipFileName;
protected internal string _GzipComment;
protected internal DateTime _GzipMtime;
protected internal int _gzipHeaderByteCount;
internal int Crc32 { get { if (crc == null) return 0; return crc.Crc32Result; } }
public ZlibBaseStream(System.IO.Stream stream,
CompressionMode compressionMode,
CompressionLevel level,
ZlibStreamFlavor flavor,
bool leaveOpen)
:this(stream, compressionMode, level, flavor,leaveOpen, ZlibConstants.WindowBitsDefault)
{ }
public ZlibBaseStream(System.IO.Stream stream,
CompressionMode compressionMode,
CompressionLevel level,
ZlibStreamFlavor flavor,
bool leaveOpen,
int windowBits)
: base()
{
this._flushMode = FlushType.None;
//this._workingBuffer = new byte[WORKING_BUFFER_SIZE_DEFAULT];
this._stream = stream;
this._leaveOpen = leaveOpen;
this._compressionMode = compressionMode;
this._flavor = flavor;
this._level = level;
this.windowBitsMax = windowBits;
// workitem 7159
if (flavor == ZlibStreamFlavor.GZIP)
{
this.crc = new BestHTTP.Decompression.Crc.CRC32();
}
}
protected internal bool _wantCompress
{
get
{
return (this._compressionMode == CompressionMode.Compress);
}
}
private ZlibCodec z
{
get
{
if (_z == null)
{
bool wantRfc1950Header = (this._flavor == ZlibStreamFlavor.ZLIB);
_z = new ZlibCodec();
if (this._compressionMode == CompressionMode.Decompress)
{
_z.InitializeInflate(this.windowBitsMax, wantRfc1950Header);
}
else
{
_z.Strategy = Strategy;
_z.InitializeDeflate(this._level, this.windowBitsMax, wantRfc1950Header);
}
}
return _z;
}
}
private byte[] workingBuffer
{
get
{
if (_workingBuffer == null)
_workingBuffer = new byte[_bufferSize];
return _workingBuffer;
}
}
public override void Write(System.Byte[] buffer, int offset, int count)
{
// workitem 7159
// calculate the CRC on the unccompressed data (before writing)
if (crc != null)
crc.SlurpBlock(buffer, offset, count);
if (_streamMode == StreamMode.Undefined)
_streamMode = StreamMode.Writer;
else if (_streamMode != StreamMode.Writer)
throw new ZlibException("Cannot Write after Reading.");
if (count == 0)
return;
// first reference of z property will initialize the private var _z
z.InputBuffer = buffer;
_z.NextIn = offset;
_z.AvailableBytesIn = count;
bool done = false;
do
{
_z.OutputBuffer = workingBuffer;
_z.NextOut = 0;
_z.AvailableBytesOut = _workingBuffer.Length;
int rc = (_wantCompress)
? _z.Deflate(_flushMode)
: _z.Inflate(_flushMode);
if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END)
throw new ZlibException((_wantCompress ? "de" : "in") + "flating: " + _z.Message);
//if (_workingBuffer.Length - _z.AvailableBytesOut > 0)
_stream.Write(_workingBuffer, 0, _workingBuffer.Length - _z.AvailableBytesOut);
done = _z.AvailableBytesIn == 0 && _z.AvailableBytesOut != 0;
// If GZIP and de-compress, we're done when 8 bytes remain.
if (_flavor == ZlibStreamFlavor.GZIP && !_wantCompress)
done = (_z.AvailableBytesIn == 8 && _z.AvailableBytesOut != 0);
}
while (!done);
}
private void finish()
{
if (_z == null) return;
if (_streamMode == StreamMode.Writer)
{
bool done = false;
do
{
_z.OutputBuffer = workingBuffer;
_z.NextOut = 0;
_z.AvailableBytesOut = _workingBuffer.Length;
int rc = (_wantCompress)
? _z.Deflate(FlushType.Finish)
: _z.Inflate(FlushType.Finish);
if (rc != ZlibConstants.Z_STREAM_END && rc != ZlibConstants.Z_OK)
{
string verb = (_wantCompress ? "de" : "in") + "flating";
if (_z.Message == null)
throw new ZlibException(String.Format("{0}: (rc = {1})", verb, rc));
else
throw new ZlibException(verb + ": " + _z.Message);
}
if (_workingBuffer.Length - _z.AvailableBytesOut > 0)
{
_stream.Write(_workingBuffer, 0, _workingBuffer.Length - _z.AvailableBytesOut);
}
done = _z.AvailableBytesIn == 0 && _z.AvailableBytesOut != 0;
// If GZIP and de-compress, we're done when 8 bytes remain.
if (_flavor == ZlibStreamFlavor.GZIP && !_wantCompress)
done = (_z.AvailableBytesIn == 8 && _z.AvailableBytesOut != 0);
}
while (!done);
Flush();
// workitem 7159
if (_flavor == ZlibStreamFlavor.GZIP)
{
if (_wantCompress)
{
// Emit the GZIP trailer: CRC32 and size mod 2^32
int c1 = crc.Crc32Result;
_stream.Write(BitConverter.GetBytes(c1), 0, 4);
int c2 = (Int32)(crc.TotalBytesRead & 0x00000000FFFFFFFF);
_stream.Write(BitConverter.GetBytes(c2), 0, 4);
}
else
{
throw new ZlibException("Writing with decompression is not supported.");
}
}
}
// workitem 7159
else if (_streamMode == StreamMode.Reader)
{
if (_flavor == ZlibStreamFlavor.GZIP)
{
if (!_wantCompress)
{
// workitem 8501: handle edge case (decompress empty stream)
if (_z.TotalBytesOut == 0L)
return;
// Read and potentially verify the GZIP trailer:
// CRC32 and size mod 2^32
byte[] trailer = new byte[8];
// workitems 8679 & 12554
if (_z.AvailableBytesIn < 8)
{
// Make sure we have read to the end of the stream
Array.Copy(_z.InputBuffer, _z.NextIn, trailer, 0, _z.AvailableBytesIn);
int bytesNeeded = 8 - _z.AvailableBytesIn;
int bytesRead = _stream.Read(trailer,
_z.AvailableBytesIn,
bytesNeeded);
if (bytesNeeded != bytesRead)
{
throw new ZlibException(String.Format("Missing or incomplete GZIP trailer. Expected 8 bytes, got {0}.",
_z.AvailableBytesIn + bytesRead));
}
}
else
{
Array.Copy(_z.InputBuffer, _z.NextIn, trailer, 0, trailer.Length);
}
Int32 crc32_expected = BitConverter.ToInt32(trailer, 0);
Int32 crc32_actual = crc.Crc32Result;
Int32 isize_expected = BitConverter.ToInt32(trailer, 4);
Int32 isize_actual = (Int32)(_z.TotalBytesOut & 0x00000000FFFFFFFF);
if (crc32_actual != crc32_expected)
throw new ZlibException(String.Format("Bad CRC32 in GZIP trailer. (actual({0:X8})!=expected({1:X8}))", crc32_actual, crc32_expected));
if (isize_actual != isize_expected)
throw new ZlibException(String.Format("Bad size in GZIP trailer. (actual({0})!=expected({1}))", isize_actual, isize_expected));
}
else
{
throw new ZlibException("Reading with compression is not supported.");
}
}
}
}
private void end()
{
if (z == null)
return;
if (_wantCompress)
{
_z.EndDeflate();
}
else
{
_z.EndInflate();
}
_z = null;
}
public
#if !NETFX_CORE
override
#endif
void Close()
{
if (_stream == null) return;
try
{
finish();
}
finally
{
end();
if (!_leaveOpen) _stream.Dispose();
_stream = null;
}
}
public override void Flush()
{
_stream.Flush();
}
public override System.Int64 Seek(System.Int64 offset, System.IO.SeekOrigin origin)
{
throw new NotImplementedException();
//_outStream.Seek(offset, origin);
}
public override void SetLength(System.Int64 value)
{
_stream.SetLength(value);
nomoreinput = false;
}
#if NOT
public int Read()
{
if (Read(_buf1, 0, 1) == 0)
return 0;
// calculate CRC after reading
if (crc!=null)
crc.SlurpBlock(_buf1,0,1);
return (_buf1[0] & 0xFF);
}
#endif
private bool nomoreinput = false;
private string ReadZeroTerminatedString()
{
var list = new System.Collections.Generic.List<byte>();
bool done = false;
do
{
// workitem 7740
int n = _stream.Read(_buf1, 0, 1);
if (n != 1)
throw new ZlibException("Unexpected EOF reading GZIP header.");
else
{
if (_buf1[0] == 0)
done = true;
else
list.Add(_buf1[0]);
}
} while (!done);
byte[] a = list.ToArray();
return GZipStream.iso8859dash1.GetString(a, 0, a.Length);
}
private int _ReadAndValidateGzipHeader()
{
int totalBytesRead = 0;
// read the header on the first read
byte[] header = new byte[10];
int n = _stream.Read(header, 0, header.Length);
// workitem 8501: handle edge case (decompress empty stream)
if (n == 0)
return 0;
if (n != 10)
throw new ZlibException("Not a valid GZIP stream.");
if (header[0] != 0x1F || header[1] != 0x8B || header[2] != 8)
throw new ZlibException("Bad GZIP header.");
Int32 timet = BitConverter.ToInt32(header, 4);
_GzipMtime = GZipStream._unixEpoch.AddSeconds(timet);
totalBytesRead += n;
if ((header[3] & 0x04) == 0x04)
{
// read and discard extra field
n = _stream.Read(header, 0, 2); // 2-byte length field
totalBytesRead += n;
Int16 extraLength = (Int16)(header[0] + header[1] * 256);
byte[] extra = new byte[extraLength];
n = _stream.Read(extra, 0, extra.Length);
if (n != extraLength)
throw new ZlibException("Unexpected end-of-file reading GZIP header.");
totalBytesRead += n;
}
if ((header[3] & 0x08) == 0x08)
_GzipFileName = ReadZeroTerminatedString();
if ((header[3] & 0x10) == 0x010)
_GzipComment = ReadZeroTerminatedString();
if ((header[3] & 0x02) == 0x02)
Read(_buf1, 0, 1); // CRC16, ignore
return totalBytesRead;
}
public override System.Int32 Read(System.Byte[] buffer, System.Int32 offset, System.Int32 count)
{
// According to MS documentation, any implementation of the IO.Stream.Read function must:
// (a) throw an exception if offset & count reference an invalid part of the buffer,
// or if count < 0, or if buffer is null
// (b) return 0 only upon EOF, or if count = 0
// (c) if not EOF, then return at least 1 byte, up to <count> bytes
if (_streamMode == StreamMode.Undefined)
{
if (!this._stream.CanRead) throw new ZlibException("The stream is not readable.");
// for the first read, set up some controls.
_streamMode = StreamMode.Reader;
// (The first reference to _z goes through the private accessor which
// may initialize it.)
z.AvailableBytesIn = 0;
if (_flavor == ZlibStreamFlavor.GZIP)
{
_gzipHeaderByteCount = _ReadAndValidateGzipHeader();
// workitem 8501: handle edge case (decompress empty stream)
if (_gzipHeaderByteCount == 0)
return 0;
}
}
if (_streamMode != StreamMode.Reader)
throw new ZlibException("Cannot Read after Writing.");
if (count == 0) return 0;
if (nomoreinput && _wantCompress) return 0; // workitem 8557
if (buffer == null) throw new ArgumentNullException("buffer");
if (count < 0) throw new ArgumentOutOfRangeException("count");
if (offset < buffer.GetLowerBound(0)) throw new ArgumentOutOfRangeException("offset");
if ((offset + count) > buffer.GetLength(0)) throw new ArgumentOutOfRangeException("count");
int rc = 0;
// set up the output of the deflate/inflate codec:
_z.OutputBuffer = buffer;
_z.NextOut = offset;
_z.AvailableBytesOut = count;
// This is necessary in case _workingBuffer has been resized. (new byte[])
// (The first reference to _workingBuffer goes through the private accessor which
// may initialize it.)
_z.InputBuffer = workingBuffer;
do
{
// need data in _workingBuffer in order to deflate/inflate. Here, we check if we have any.
if ((_z.AvailableBytesIn == 0) && (!nomoreinput))
{
// No data available, so try to Read data from the captive stream.
_z.NextIn = 0;
_z.AvailableBytesIn = _stream.Read(_workingBuffer, 0, _workingBuffer.Length);
if (_z.AvailableBytesIn == 0)
nomoreinput = true;
}
// we have data in InputBuffer; now compress or decompress as appropriate
rc = (_wantCompress)
? _z.Deflate(_flushMode)
: _z.Inflate(_flushMode);
if (nomoreinput && (rc == ZlibConstants.Z_BUF_ERROR))
return 0;
if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END)
throw new ZlibException(String.Format("{0}flating: rc={1} msg={2}", (_wantCompress ? "de" : "in"), rc, _z.Message));
if ((nomoreinput || rc == ZlibConstants.Z_STREAM_END) && (_z.AvailableBytesOut == count))
break; // nothing more to read
}
//while (_z.AvailableBytesOut == count && rc == ZlibConstants.Z_OK);
while (_z.AvailableBytesOut > 0 && !nomoreinput && rc == ZlibConstants.Z_OK);
// workitem 8557
// is there more room in output?
if (_z.AvailableBytesOut > 0)
{
if (rc == ZlibConstants.Z_OK && _z.AvailableBytesIn == 0)
{
// deferred
}
// are we completely done reading?
if (nomoreinput)
{
// and in compression?
if (_wantCompress)
{
// no more input data available; therefore we flush to
// try to complete the read
rc = _z.Deflate(FlushType.Finish);
if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END)
throw new ZlibException(String.Format("Deflating: rc={0} msg={1}", rc, _z.Message));
}
}
}
rc = (count - _z.AvailableBytesOut);
// calculate CRC after reading
if (crc != null)
crc.SlurpBlock(buffer, offset, rc);
return rc;
}
public override System.Boolean CanRead
{
get { return this._stream.CanRead; }
}
public override System.Boolean CanSeek
{
get { return this._stream.CanSeek; }
}
public override System.Boolean CanWrite
{
get { return this._stream.CanWrite; }
}
public override System.Int64 Length
{
get { return _stream.Length; }
}
public override long Position
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
internal enum StreamMode
{
Writer,
Reader,
Undefined,
}
public static void CompressString(String s, Stream compressor)
{
byte[] uncompressed = System.Text.Encoding.UTF8.GetBytes(s);
using (compressor)
{
compressor.Write(uncompressed, 0, uncompressed.Length);
}
}
public static void CompressBuffer(byte[] b, Stream compressor)
{
// workitem 8460
using (compressor)
{
compressor.Write(b, 0, b.Length);
}
}
public static String UncompressString(byte[] compressed, Stream decompressor)
{
// workitem 8460
byte[] working = new byte[1024];
var encoding = System.Text.Encoding.UTF8;
using (var output = new MemoryStream())
{
using (decompressor)
{
int n;
while ((n = decompressor.Read(working, 0, working.Length)) != 0)
{
output.Write(working, 0, n);
}
}
// reset to allow read from start
output.Seek(0, SeekOrigin.Begin);
var sr = new StreamReader(output, encoding);
return sr.ReadToEnd();
}
}
public static byte[] UncompressBuffer(byte[] compressed, Stream decompressor)
{
// workitem 8460
byte[] working = new byte[1024];
using (var output = new MemoryStream())
{
using (decompressor)
{
int n;
while ((n = decompressor.Read(working, 0, working.Length)) != 0)
{
output.Write(working, 0, n);
}
}
return output.ToArray();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4778dec0f7e464e458328e8449dde472
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,712 @@
// ZlibCodec.cs
// ------------------------------------------------------------------
//
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
// All rights reserved.
//
// This code module is part of DotNetZip, a zipfile class library.
//
// ------------------------------------------------------------------
//
// This code is licensed under the Microsoft Public License.
// See the file License.txt for the license details.
// More info on: http://dotnetzip.codeplex.com
//
// ------------------------------------------------------------------
//
// last saved (in emacs):
// Time-stamp: <2009-November-03 15:40:51>
//
// ------------------------------------------------------------------
//
// This module defines a Codec for ZLIB compression and
// decompression. This code extends code that was based the jzlib
// implementation of zlib, but this code is completely novel. The codec
// class is new, and encapsulates some behaviors that are new, and some
// that were present in other classes in the jzlib code base. In
// keeping with the license for jzlib, the copyright to the jzlib code
// is included below.
//
// ------------------------------------------------------------------
//
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in
// the documentation and/or other materials provided with the distribution.
//
// 3. The names of the authors may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// -----------------------------------------------------------------------
//
// This program is based on zlib-1.1.3; credit to authors
// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
// and contributors of zlib.
//
// -----------------------------------------------------------------------
using System;
using Interop=System.Runtime.InteropServices;
namespace BestHTTP.Decompression.Zlib
{
/// <summary>
/// Encoder and Decoder for ZLIB and DEFLATE (IETF RFC1950 and RFC1951).
/// </summary>
///
/// <remarks>
/// This class compresses and decompresses data according to the Deflate algorithm
/// and optionally, the ZLIB format, as documented in <see
/// href="http://www.ietf.org/rfc/rfc1950.txt">RFC 1950 - ZLIB</see> and <see
/// href="http://www.ietf.org/rfc/rfc1951.txt">RFC 1951 - DEFLATE</see>.
/// </remarks>
sealed internal class ZlibCodec
{
/// <summary>
/// The buffer from which data is taken.
/// </summary>
public byte[] InputBuffer;
/// <summary>
/// An index into the InputBuffer array, indicating where to start reading.
/// </summary>
public int NextIn;
/// <summary>
/// The number of bytes available in the InputBuffer, starting at NextIn.
/// </summary>
/// <remarks>
/// Generally you should set this to InputBuffer.Length before the first Inflate() or Deflate() call.
/// The class will update this number as calls to Inflate/Deflate are made.
/// </remarks>
public int AvailableBytesIn;
/// <summary>
/// Total number of bytes read so far, through all calls to Inflate()/Deflate().
/// </summary>
public long TotalBytesIn;
/// <summary>
/// Buffer to store output data.
/// </summary>
public byte[] OutputBuffer;
/// <summary>
/// An index into the OutputBuffer array, indicating where to start writing.
/// </summary>
public int NextOut;
/// <summary>
/// The number of bytes available in the OutputBuffer, starting at NextOut.
/// </summary>
/// <remarks>
/// Generally you should set this to OutputBuffer.Length before the first Inflate() or Deflate() call.
/// The class will update this number as calls to Inflate/Deflate are made.
/// </remarks>
public int AvailableBytesOut;
/// <summary>
/// Total number of bytes written to the output so far, through all calls to Inflate()/Deflate().
/// </summary>
public long TotalBytesOut;
/// <summary>
/// used for diagnostics, when something goes wrong!
/// </summary>
public System.String Message;
internal DeflateManager dstate;
internal InflateManager istate;
internal uint _Adler32;
/// <summary>
/// The compression level to use in this codec. Useful only in compression mode.
/// </summary>
public CompressionLevel CompressLevel = CompressionLevel.Default;
/// <summary>
/// The number of Window Bits to use.
/// </summary>
/// <remarks>
/// This gauges the size of the sliding window, and hence the
/// compression effectiveness as well as memory consumption. It's best to just leave this
/// setting alone if you don't know what it is. The maximum value is 15 bits, which implies
/// a 32k window.
/// </remarks>
public int WindowBits = ZlibConstants.WindowBitsDefault;
/// <summary>
/// The compression strategy to use.
/// </summary>
/// <remarks>
/// This is only effective in compression. The theory offered by ZLIB is that different
/// strategies could potentially produce significant differences in compression behavior
/// for different data sets. Unfortunately I don't have any good recommendations for how
/// to set it differently. When I tested changing the strategy I got minimally different
/// compression performance. It's best to leave this property alone if you don't have a
/// good feel for it. Or, you may want to produce a test harness that runs through the
/// different strategy options and evaluates them on different file types. If you do that,
/// let me know your results.
/// </remarks>
public CompressionStrategy Strategy = CompressionStrategy.Default;
/// <summary>
/// The Adler32 checksum on the data transferred through the codec so far. You probably don't need to look at this.
/// </summary>
public int Adler32 { get { return (int)_Adler32; } }
/// <summary>
/// Create a ZlibCodec.
/// </summary>
/// <remarks>
/// If you use this default constructor, you will later have to explicitly call
/// InitializeInflate() or InitializeDeflate() before using the ZlibCodec to compress
/// or decompress.
/// </remarks>
public ZlibCodec() { }
/// <summary>
/// Create a ZlibCodec that either compresses or decompresses.
/// </summary>
/// <param name="mode">
/// Indicates whether the codec should compress (deflate) or decompress (inflate).
/// </param>
public ZlibCodec(CompressionMode mode)
{
if (mode == CompressionMode.Compress)
{
int rc = InitializeDeflate();
if (rc != ZlibConstants.Z_OK) throw new ZlibException("Cannot initialize for deflate.");
}
else if (mode == CompressionMode.Decompress)
{
int rc = InitializeInflate();
if (rc != ZlibConstants.Z_OK) throw new ZlibException("Cannot initialize for inflate.");
}
else throw new ZlibException("Invalid ZlibStreamFlavor.");
}
/// <summary>
/// Initialize the inflation state.
/// </summary>
/// <remarks>
/// It is not necessary to call this before using the ZlibCodec to inflate data;
/// It is implicitly called when you call the constructor.
/// </remarks>
/// <returns>Z_OK if everything goes well.</returns>
public int InitializeInflate()
{
return InitializeInflate(this.WindowBits);
}
/// <summary>
/// Initialize the inflation state with an explicit flag to
/// govern the handling of RFC1950 header bytes.
/// </summary>
///
/// <remarks>
/// By default, the ZLIB header defined in <see
/// href="http://www.ietf.org/rfc/rfc1950.txt">RFC 1950</see> is expected. If
/// you want to read a zlib stream you should specify true for
/// expectRfc1950Header. If you have a deflate stream, you will want to specify
/// false. It is only necessary to invoke this initializer explicitly if you
/// want to specify false.
/// </remarks>
///
/// <param name="expectRfc1950Header">whether to expect an RFC1950 header byte
/// pair when reading the stream of data to be inflated.</param>
///
/// <returns>Z_OK if everything goes well.</returns>
public int InitializeInflate(bool expectRfc1950Header)
{
return InitializeInflate(this.WindowBits, expectRfc1950Header);
}
/// <summary>
/// Initialize the ZlibCodec for inflation, with the specified number of window bits.
/// </summary>
/// <param name="windowBits">The number of window bits to use. If you need to ask what that is,
/// then you shouldn't be calling this initializer.</param>
/// <returns>Z_OK if all goes well.</returns>
public int InitializeInflate(int windowBits)
{
this.WindowBits = windowBits;
return InitializeInflate(windowBits, true);
}
/// <summary>
/// Initialize the inflation state with an explicit flag to govern the handling of
/// RFC1950 header bytes.
/// </summary>
///
/// <remarks>
/// If you want to read a zlib stream you should specify true for
/// expectRfc1950Header. In this case, the library will expect to find a ZLIB
/// header, as defined in <see href="http://www.ietf.org/rfc/rfc1950.txt">RFC
/// 1950</see>, in the compressed stream. If you will be reading a DEFLATE or
/// GZIP stream, which does not have such a header, you will want to specify
/// false.
/// </remarks>
///
/// <param name="expectRfc1950Header">whether to expect an RFC1950 header byte pair when reading
/// the stream of data to be inflated.</param>
/// <param name="windowBits">The number of window bits to use. If you need to ask what that is,
/// then you shouldn't be calling this initializer.</param>
/// <returns>Z_OK if everything goes well.</returns>
public int InitializeInflate(int windowBits, bool expectRfc1950Header)
{
this.WindowBits = windowBits;
if (dstate != null) throw new ZlibException("You may not call InitializeInflate() after calling InitializeDeflate().");
istate = new InflateManager(expectRfc1950Header);
return istate.Initialize(this, windowBits);
}
/// <summary>
/// Inflate the data in the InputBuffer, placing the result in the OutputBuffer.
/// </summary>
/// <remarks>
/// You must have set InputBuffer and OutputBuffer, NextIn and NextOut, and AvailableBytesIn and
/// AvailableBytesOut before calling this method.
/// </remarks>
/// <example>
/// <code>
/// private void InflateBuffer()
/// {
/// int bufferSize = 1024;
/// byte[] buffer = new byte[bufferSize];
/// ZlibCodec decompressor = new ZlibCodec();
///
/// Console.WriteLine("\n============================================");
/// Console.WriteLine("Size of Buffer to Inflate: {0} bytes.", CompressedBytes.Length);
/// MemoryStream ms = new MemoryStream(DecompressedBytes);
///
/// int rc = decompressor.InitializeInflate();
///
/// decompressor.InputBuffer = CompressedBytes;
/// decompressor.NextIn = 0;
/// decompressor.AvailableBytesIn = CompressedBytes.Length;
///
/// decompressor.OutputBuffer = buffer;
///
/// // pass 1: inflate
/// do
/// {
/// decompressor.NextOut = 0;
/// decompressor.AvailableBytesOut = buffer.Length;
/// rc = decompressor.Inflate(FlushType.None);
///
/// if (rc != ZlibConstants.Z_OK &amp;&amp; rc != ZlibConstants.Z_STREAM_END)
/// throw new Exception("inflating: " + decompressor.Message);
///
/// ms.Write(decompressor.OutputBuffer, 0, buffer.Length - decompressor.AvailableBytesOut);
/// }
/// while (decompressor.AvailableBytesIn &gt; 0 || decompressor.AvailableBytesOut == 0);
///
/// // pass 2: finish and flush
/// do
/// {
/// decompressor.NextOut = 0;
/// decompressor.AvailableBytesOut = buffer.Length;
/// rc = decompressor.Inflate(FlushType.Finish);
///
/// if (rc != ZlibConstants.Z_STREAM_END &amp;&amp; rc != ZlibConstants.Z_OK)
/// throw new Exception("inflating: " + decompressor.Message);
///
/// if (buffer.Length - decompressor.AvailableBytesOut &gt; 0)
/// ms.Write(buffer, 0, buffer.Length - decompressor.AvailableBytesOut);
/// }
/// while (decompressor.AvailableBytesIn &gt; 0 || decompressor.AvailableBytesOut == 0);
///
/// decompressor.EndInflate();
/// }
///
/// </code>
/// </example>
/// <param name="flush">The flush to use when inflating.</param>
/// <returns>Z_OK if everything goes well.</returns>
public int Inflate(FlushType flush)
{
if (istate == null)
throw new ZlibException("No Inflate State!");
return istate.Inflate(flush);
}
/// <summary>
/// Ends an inflation session.
/// </summary>
/// <remarks>
/// Call this after successively calling Inflate(). This will cause all buffers to be flushed.
/// After calling this you cannot call Inflate() without a intervening call to one of the
/// InitializeInflate() overloads.
/// </remarks>
/// <returns>Z_OK if everything goes well.</returns>
public int EndInflate()
{
if (istate == null)
throw new ZlibException("No Inflate State!");
int ret = istate.End();
istate = null;
return ret;
}
/// <summary>
/// I don't know what this does!
/// </summary>
/// <returns>Z_OK if everything goes well.</returns>
public int SyncInflate()
{
if (istate == null)
throw new ZlibException("No Inflate State!");
return istate.Sync();
}
/// <summary>
/// Initialize the ZlibCodec for deflation operation.
/// </summary>
/// <remarks>
/// The codec will use the MAX window bits and the default level of compression.
/// </remarks>
/// <example>
/// <code>
/// int bufferSize = 40000;
/// byte[] CompressedBytes = new byte[bufferSize];
/// byte[] DecompressedBytes = new byte[bufferSize];
///
/// ZlibCodec compressor = new ZlibCodec();
///
/// compressor.InitializeDeflate(CompressionLevel.Default);
///
/// compressor.InputBuffer = System.Text.ASCIIEncoding.ASCII.GetBytes(TextToCompress);
/// compressor.NextIn = 0;
/// compressor.AvailableBytesIn = compressor.InputBuffer.Length;
///
/// compressor.OutputBuffer = CompressedBytes;
/// compressor.NextOut = 0;
/// compressor.AvailableBytesOut = CompressedBytes.Length;
///
/// while (compressor.TotalBytesIn != TextToCompress.Length &amp;&amp; compressor.TotalBytesOut &lt; bufferSize)
/// {
/// compressor.Deflate(FlushType.None);
/// }
///
/// while (true)
/// {
/// int rc= compressor.Deflate(FlushType.Finish);
/// if (rc == ZlibConstants.Z_STREAM_END) break;
/// }
///
/// compressor.EndDeflate();
///
/// </code>
/// </example>
/// <returns>Z_OK if all goes well. You generally don't need to check the return code.</returns>
public int InitializeDeflate()
{
return _InternalInitializeDeflate(true);
}
/// <summary>
/// Initialize the ZlibCodec for deflation operation, using the specified CompressionLevel.
/// </summary>
/// <remarks>
/// The codec will use the maximum window bits (15) and the specified
/// CompressionLevel. It will emit a ZLIB stream as it compresses.
/// </remarks>
/// <param name="level">The compression level for the codec.</param>
/// <returns>Z_OK if all goes well.</returns>
public int InitializeDeflate(CompressionLevel level)
{
this.CompressLevel = level;
return _InternalInitializeDeflate(true);
}
/// <summary>
/// Initialize the ZlibCodec for deflation operation, using the specified CompressionLevel,
/// and the explicit flag governing whether to emit an RFC1950 header byte pair.
/// </summary>
/// <remarks>
/// The codec will use the maximum window bits (15) and the specified CompressionLevel.
/// If you want to generate a zlib stream, you should specify true for
/// wantRfc1950Header. In this case, the library will emit a ZLIB
/// header, as defined in <see href="http://www.ietf.org/rfc/rfc1950.txt">RFC
/// 1950</see>, in the compressed stream.
/// </remarks>
/// <param name="level">The compression level for the codec.</param>
/// <param name="wantRfc1950Header">whether to emit an initial RFC1950 byte pair in the compressed stream.</param>
/// <returns>Z_OK if all goes well.</returns>
public int InitializeDeflate(CompressionLevel level, bool wantRfc1950Header)
{
this.CompressLevel = level;
return _InternalInitializeDeflate(wantRfc1950Header);
}
/// <summary>
/// Initialize the ZlibCodec for deflation operation, using the specified CompressionLevel,
/// and the specified number of window bits.
/// </summary>
/// <remarks>
/// The codec will use the specified number of window bits and the specified CompressionLevel.
/// </remarks>
/// <param name="level">The compression level for the codec.</param>
/// <param name="bits">the number of window bits to use. If you don't know what this means, don't use this method.</param>
/// <returns>Z_OK if all goes well.</returns>
public int InitializeDeflate(CompressionLevel level, int bits)
{
this.CompressLevel = level;
this.WindowBits = bits;
return _InternalInitializeDeflate(true);
}
/// <summary>
/// Initialize the ZlibCodec for deflation operation, using the specified
/// CompressionLevel, the specified number of window bits, and the explicit flag
/// governing whether to emit an RFC1950 header byte pair.
/// </summary>
///
/// <param name="level">The compression level for the codec.</param>
/// <param name="wantRfc1950Header">whether to emit an initial RFC1950 byte pair in the compressed stream.</param>
/// <param name="bits">the number of window bits to use. If you don't know what this means, don't use this method.</param>
/// <returns>Z_OK if all goes well.</returns>
public int InitializeDeflate(CompressionLevel level, int bits, bool wantRfc1950Header)
{
this.CompressLevel = level;
this.WindowBits = bits;
return _InternalInitializeDeflate(wantRfc1950Header);
}
private int _InternalInitializeDeflate(bool wantRfc1950Header)
{
if (istate != null) throw new ZlibException("You may not call InitializeDeflate() after calling InitializeInflate().");
dstate = new DeflateManager();
dstate.WantRfc1950HeaderBytes = wantRfc1950Header;
return dstate.Initialize(this, this.CompressLevel, this.WindowBits, this.Strategy);
}
/// <summary>
/// Deflate one batch of data.
/// </summary>
/// <remarks>
/// You must have set InputBuffer and OutputBuffer before calling this method.
/// </remarks>
/// <example>
/// <code>
/// private void DeflateBuffer(CompressionLevel level)
/// {
/// int bufferSize = 1024;
/// byte[] buffer = new byte[bufferSize];
/// ZlibCodec compressor = new ZlibCodec();
///
/// Console.WriteLine("\n============================================");
/// Console.WriteLine("Size of Buffer to Deflate: {0} bytes.", UncompressedBytes.Length);
/// MemoryStream ms = new MemoryStream();
///
/// int rc = compressor.InitializeDeflate(level);
///
/// compressor.InputBuffer = UncompressedBytes;
/// compressor.NextIn = 0;
/// compressor.AvailableBytesIn = UncompressedBytes.Length;
///
/// compressor.OutputBuffer = buffer;
///
/// // pass 1: deflate
/// do
/// {
/// compressor.NextOut = 0;
/// compressor.AvailableBytesOut = buffer.Length;
/// rc = compressor.Deflate(FlushType.None);
///
/// if (rc != ZlibConstants.Z_OK &amp;&amp; rc != ZlibConstants.Z_STREAM_END)
/// throw new Exception("deflating: " + compressor.Message);
///
/// ms.Write(compressor.OutputBuffer, 0, buffer.Length - compressor.AvailableBytesOut);
/// }
/// while (compressor.AvailableBytesIn &gt; 0 || compressor.AvailableBytesOut == 0);
///
/// // pass 2: finish and flush
/// do
/// {
/// compressor.NextOut = 0;
/// compressor.AvailableBytesOut = buffer.Length;
/// rc = compressor.Deflate(FlushType.Finish);
///
/// if (rc != ZlibConstants.Z_STREAM_END &amp;&amp; rc != ZlibConstants.Z_OK)
/// throw new Exception("deflating: " + compressor.Message);
///
/// if (buffer.Length - compressor.AvailableBytesOut &gt; 0)
/// ms.Write(buffer, 0, buffer.Length - compressor.AvailableBytesOut);
/// }
/// while (compressor.AvailableBytesIn &gt; 0 || compressor.AvailableBytesOut == 0);
///
/// compressor.EndDeflate();
///
/// ms.Seek(0, SeekOrigin.Begin);
/// CompressedBytes = new byte[compressor.TotalBytesOut];
/// ms.Read(CompressedBytes, 0, CompressedBytes.Length);
/// }
/// </code>
/// </example>
/// <param name="flush">whether to flush all data as you deflate. Generally you will want to
/// use Z_NO_FLUSH here, in a series of calls to Deflate(), and then call EndDeflate() to
/// flush everything.
/// </param>
/// <returns>Z_OK if all goes well.</returns>
public int Deflate(FlushType flush)
{
if (dstate == null)
throw new ZlibException("No Deflate State!");
return dstate.Deflate(flush);
}
/// <summary>
/// End a deflation session.
/// </summary>
/// <remarks>
/// Call this after making a series of one or more calls to Deflate(). All buffers are flushed.
/// </remarks>
/// <returns>Z_OK if all goes well.</returns>
public int EndDeflate()
{
if (dstate == null)
throw new ZlibException("No Deflate State!");
// TODO: dinoch Tue, 03 Nov 2009 15:39 (test this)
//int ret = dstate.End();
dstate = null;
return ZlibConstants.Z_OK; //ret;
}
/// <summary>
/// Reset a codec for another deflation session.
/// </summary>
/// <remarks>
/// Call this to reset the deflation state. For example if a thread is deflating
/// non-consecutive blocks, you can call Reset() after the Deflate(Sync) of the first
/// block and before the next Deflate(None) of the second block.
/// </remarks>
/// <returns>Z_OK if all goes well.</returns>
public void ResetDeflate()
{
if (dstate == null)
throw new ZlibException("No Deflate State!");
dstate.Reset();
}
/// <summary>
/// Set the CompressionStrategy and CompressionLevel for a deflation session.
/// </summary>
/// <param name="level">the level of compression to use.</param>
/// <param name="strategy">the strategy to use for compression.</param>
/// <returns>Z_OK if all goes well.</returns>
public int SetDeflateParams(CompressionLevel level, CompressionStrategy strategy)
{
if (dstate == null)
throw new ZlibException("No Deflate State!");
return dstate.SetParams(level, strategy);
}
/// <summary>
/// Set the dictionary to be used for either Inflation or Deflation.
/// </summary>
/// <param name="dictionary">The dictionary bytes to use.</param>
/// <returns>Z_OK if all goes well.</returns>
public int SetDictionary(byte[] dictionary)
{
if (istate != null)
return istate.SetDictionary(dictionary);
if (dstate != null)
return dstate.SetDictionary(dictionary);
throw new ZlibException("No Inflate or Deflate state!");
}
// Flush as much pending output as possible. All deflate() output goes
// through this function so some applications may wish to modify it
// to avoid allocating a large strm->next_out buffer and copying into it.
// (See also read_buf()).
internal void flush_pending()
{
int len = dstate.pendingCount;
if (len > AvailableBytesOut)
len = AvailableBytesOut;
if (len == 0)
return;
if (dstate.pending.Length <= dstate.nextPending ||
OutputBuffer.Length <= NextOut ||
dstate.pending.Length < (dstate.nextPending + len) ||
OutputBuffer.Length < (NextOut + len))
{
throw new ZlibException(String.Format("Invalid State. (pending.Length={0}, pendingCount={1})",
dstate.pending.Length, dstate.pendingCount));
}
Array.Copy(dstate.pending, dstate.nextPending, OutputBuffer, NextOut, len);
NextOut += len;
dstate.nextPending += len;
TotalBytesOut += len;
AvailableBytesOut -= len;
dstate.pendingCount -= len;
if (dstate.pendingCount == 0)
{
dstate.nextPending = 0;
}
}
// Read a new buffer from the current input stream, update the adler32
// and total number of bytes read. All deflate() input goes through
// this function so some applications may wish to modify it to avoid
// allocating a large strm->next_in buffer and copying from it.
// (See also flush_pending()).
internal int read_buf(byte[] buf, int start, int size)
{
int len = AvailableBytesIn;
if (len > size)
len = size;
if (len == 0)
return 0;
AvailableBytesIn -= len;
if (dstate.WantRfc1950HeaderBytes)
{
_Adler32 = Adler.Adler32(_Adler32, InputBuffer, NextIn, len);
}
Array.Copy(InputBuffer, NextIn, buf, start, len);
NextIn += len;
TotalBytesIn += len;
return len;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: daadae6429903432687293b9b403617b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,128 @@
// ZlibConstants.cs
// ------------------------------------------------------------------
//
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
// All rights reserved.
//
// This code module is part of DotNetZip, a zipfile class library.
//
// ------------------------------------------------------------------
//
// This code is licensed under the Microsoft Public License.
// See the file License.txt for the license details.
// More info on: http://dotnetzip.codeplex.com
//
// ------------------------------------------------------------------
//
// last saved (in emacs):
// Time-stamp: <2009-November-03 18:50:19>
//
// ------------------------------------------------------------------
//
// This module defines constants used by the zlib class library. This
// code is derived from the jzlib implementation of zlib, but
// significantly modified. In keeping with the license for jzlib, the
// copyright to that code is included here.
//
// ------------------------------------------------------------------
//
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in
// the documentation and/or other materials provided with the distribution.
//
// 3. The names of the authors may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// -----------------------------------------------------------------------
//
// This program is based on zlib-1.1.3; credit to authors
// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
// and contributors of zlib.
//
// -----------------------------------------------------------------------
using System;
namespace BestHTTP.Decompression.Zlib
{
/// <summary>
/// A bunch of constants used in the Zlib interface.
/// </summary>
public static class ZlibConstants
{
/// <summary>
/// The maximum number of window bits for the Deflate algorithm.
/// </summary>
public const int WindowBitsMax = 15; // 32K LZ77 window
/// <summary>
/// The default number of window bits for the Deflate algorithm.
/// </summary>
public const int WindowBitsDefault = WindowBitsMax;
/// <summary>
/// indicates everything is A-OK
/// </summary>
public const int Z_OK = 0;
/// <summary>
/// Indicates that the last operation reached the end of the stream.
/// </summary>
public const int Z_STREAM_END = 1;
/// <summary>
/// The operation ended in need of a dictionary.
/// </summary>
public const int Z_NEED_DICT = 2;
/// <summary>
/// There was an error with the stream - not enough data, not open and readable, etc.
/// </summary>
public const int Z_STREAM_ERROR = -2;
/// <summary>
/// There was an error with the data - not enough data, bad data, etc.
/// </summary>
public const int Z_DATA_ERROR = -3;
/// <summary>
/// There was an error with the working buffer.
/// </summary>
public const int Z_BUF_ERROR = -5;
/// <summary>
/// The size of the working buffer used in the ZlibCodec class. Defaults to 8192 bytes.
/// </summary>
#if NETCF
public const int WorkingBufferSizeDefault = 8192;
#else
public const int WorkingBufferSizeDefault = 16384;
#endif
/// <summary>
/// The minimum size of the working buffer used in the ZlibCodec class. Currently it is 128 bytes.
/// </summary>
public const int WorkingBufferSizeMin = 1024;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4a45dbe56e7174fda8db608fd32af391
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 08a0093ae079a433f9583a946c7573d6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,407 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
#if NETFX_CORE
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
using Windows.Storage.Streams;
using BestHTTP.PlatformSupport.IO;
using FileStream = BestHTTP.PlatformSupport.IO.FileStream;
#elif UNITY_WP8
using Cryptography = BestHTTP.PlatformSupport.Cryptography;
#else
using Cryptography = System.Security.Cryptography;
using FileStream = System.IO.FileStream;
#endif
namespace BestHTTP.Extensions
{
public static class Extensions
{
#region ASCII Encoding (These are required because Windows Phone doesn't supports the Encoding.ASCII class.)
/// <summary>
/// On WP8 platform there are no ASCII encoding.
/// </summary>
public static string AsciiToString(this byte[] bytes)
{
StringBuilder sb = new StringBuilder(bytes.Length);
foreach (byte b in bytes)
sb.Append(b <= 0x7f ? (char)b : '?');
return sb.ToString();
}
/// <summary>
/// On WP8 platform there are no ASCII encoding.
/// </summary>
public static byte[] GetASCIIBytes(this string str)
{
byte[] result = new byte[str.Length];
for (int i = 0; i < str.Length; ++i)
{
char ch = str[i];
result[i] = (byte)((ch < (char)0x80) ? ch : '?');
}
return result;
}
public static void SendAsASCII(this BinaryWriter stream, string str)
{
for (int i = 0; i < str.Length; ++i)
{
char ch = str[i];
stream.Write((byte)((ch < (char)0x80) ? ch : '?'));
}
}
#endregion
#region FileSystem WriteLine function support
public static void WriteLine(this FileStream fs)
{
fs.Write(HTTPRequest.EOL, 0, 2);
}
public static void WriteLine(this FileStream fs, string line)
{
var buff = line.GetASCIIBytes();
fs.Write(buff, 0, buff.Length);
fs.WriteLine();
}
public static void WriteLine(this FileStream fs, string format, params object[] values)
{
var buff = string.Format(format, values).GetASCIIBytes();
fs.Write(buff, 0, buff.Length);
fs.WriteLine();
}
#endregion
#region Other Extensions
public static string GetRequestPathAndQueryURL(this Uri uri)
{
string requestPathAndQuery = uri.GetComponents(UriComponents.PathAndQuery, UriFormat.UriEscaped);
// http://forum.unity3d.com/threads/best-http-released.200006/page-26#post-2723250
if (string.IsNullOrEmpty(requestPathAndQuery))
requestPathAndQuery = "/";
return requestPathAndQuery;
}
public static string[] FindOption(this string str, string option)
{
//s-maxage=2678400, must-revalidate, max-age=0
string[] options = str.ToLower().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
option = option.ToLower();
for (int i = 0; i < options.Length; ++i)
if (options[i].Contains(option))
return options[i].Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
return null;
}
public static void WriteArray(this Stream stream, byte[] array)
{
stream.Write(array, 0, array.Length);
}
#endregion
#region String Conversions
public static int ToInt32(this string str, int defaultValue = default(int))
{
if (str == null)
return defaultValue;
try
{
return int.Parse(str);
}
catch
{
return defaultValue;
}
}
public static long ToInt64(this string str, long defaultValue = default(long))
{
if (str == null)
return defaultValue;
try
{
return long.Parse(str);
}
catch
{
return defaultValue;
}
}
public static DateTime ToDateTime(this string str, DateTime defaultValue = default(DateTime))
{
if (str == null)
return defaultValue;
try
{
DateTime.TryParse(str, out defaultValue);
return defaultValue.ToUniversalTime();
}
catch
{
return defaultValue;
}
}
public static string ToStrOrEmpty(this string str)
{
if (str == null)
return String.Empty;
return str;
}
#endregion
#region MD5 Hashing
public static string CalculateMD5Hash(this string input)
{
return input.GetASCIIBytes().CalculateMD5Hash();
}
public static string CalculateMD5Hash(this byte[] input)
{
#if NETFX_CORE
var alg = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Md5);
IBuffer buff = CryptographicBuffer.CreateFromByteArray(input);
var hashed = alg.HashData(buff);
var res = CryptographicBuffer.EncodeToHexString(hashed);
return res;
#else
var hash = Cryptography.MD5.Create().ComputeHash(input);
var sb = new StringBuilder();
foreach (var b in hash)
sb.Append(b.ToString("x2"));
return sb.ToString();
#endif
}
#endregion
#region Efficient String Parsing Helpers
internal static string Read(this string str, ref int pos, char block, bool needResult = true)
{
return str.Read(ref pos, (ch) => ch != block, needResult);
}
internal static string Read(this string str, ref int pos, Func<char, bool> block, bool needResult = true)
{
if (pos >= str.Length)
return string.Empty;
str.SkipWhiteSpace(ref pos);
int startPos = pos;
while (pos < str.Length && block(str[pos]))
pos++;
string result = needResult ? str.Substring(startPos, pos - startPos) : null;
// set position to the next char
pos++;
return result;
}
internal static string ReadPossibleQuotedText(this string str, ref int pos)
{
string result = string.Empty;
if (str == null)
return result;
// It's a quoted text?
if (str[pos] == '\"')
{
// Skip the starting quote
str.Read(ref pos, '\"', false);
// Read the text until the ending quote
result = str.Read(ref pos, '\"');
// Next option
str.Read(ref pos, ',', false);
}
else
// It's not a quoted text, so we will read until the next option
result = str.Read(ref pos, (ch) => ch != ',' && ch != ';');
return result;
}
internal static void SkipWhiteSpace(this string str, ref int pos)
{
if (pos >= str.Length)
return;
while (pos < str.Length && char.IsWhiteSpace(str[pos]))
pos++;
}
internal static string TrimAndLower(this string str)
{
if (str == null)
return null;
char[] buffer = new char[str.Length];
int length = 0;
for (int i = 0; i < str.Length; ++i)
{
char ch = str[i];
if (!char.IsWhiteSpace(ch) && !char.IsControl(ch))
buffer[length++] = char.ToLowerInvariant(ch);
}
return new string(buffer, 0, length);
}
internal static char? Peek(this string str, int pos)
{
if (pos < 0 || pos >= str.Length)
return null;
return str[pos];
}
#endregion
#region Specialized String Parsers
//public, max-age=2592000
internal static List<HeaderValue> ParseOptionalHeader(this 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 != ',').TrimAndLower();
HeaderValue qp = new HeaderValue(key);
if (str[idx - 1] == '=')
qp.Value = str.ReadPossibleQuotedText(ref idx);
result.Add(qp);
}
return result;
}
//deflate, gzip, x-gzip, identity, *;q=0
internal static List<HeaderValue> ParseQualityParams(this string str)
{
List<HeaderValue> result = new List<HeaderValue>();
if (str == null)
return result;
int idx = 0;
while (idx < str.Length)
{
string key = str.Read(ref idx, (ch) => ch != ',' && ch != ';').TrimAndLower();
HeaderValue qp = new HeaderValue(key);
if (str[idx - 1] == ';')
{
str.Read(ref idx, '=', false);
qp.Value = str.Read(ref idx, ',');
}
result.Add(qp);
}
return result;
}
#endregion
#region Buffer Filling
/// <summary>
/// Will fill the entire buffer from the stream. Will throw an exception when the underlying stream is closed.
/// </summary>
public static void ReadBuffer(this Stream stream, byte[] buffer)
{
int count = 0;
do
{
int read = stream.Read(buffer, count, buffer.Length - count);
if (read <= 0)
throw ExceptionHelper.ServerClosedTCPStream();
count += read;
} while (count < buffer.Length);
}
#endregion
#region MemoryStream
public static void WriteAll(this MemoryStream ms, byte[] buffer)
{
ms.Write(buffer, 0, buffer.Length);
}
public static void WriteString(this MemoryStream ms, string str)
{
byte[] buffer = Encoding.UTF8.GetBytes(str);
ms.WriteAll(buffer);
}
public static void WriteLine(this MemoryStream ms)
{
ms.WriteAll(HTTPRequest.EOL);
}
public static void WriteLine(this MemoryStream ms, string str)
{
ms.WriteString(str);
ms.WriteLine();
}
#endregion
}
public static class ExceptionHelper
{
public static Exception ServerClosedTCPStream()
{
return new Exception("TCP Stream closed unexpectedly by the remote server");
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 61f3ad8711ee4471d8e759861c7e3cb3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,40 @@
using System.Collections.Generic;
namespace BestHTTP.Extensions
{
/// <summary>
/// Will parse a comma-separeted header value
/// </summary>
public sealed class HeaderParser : KeyValuePairList
{
public HeaderParser(string headerStr)
{
base.Values = Parse(headerStr);
}
private List<HeaderValue> Parse(string headerStr)
{
List<HeaderValue> result = new List<HeaderValue>();
int pos = 0;
try
{
while (pos < headerStr.Length)
{
HeaderValue current = new HeaderValue();
current.Parse(headerStr, ref pos);
result.Add(current);
}
}
catch(System.Exception ex)
{
HTTPManager.Logger.Exception("HeaderParser - Parse", headerStr, ex);
}
return result;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5ec9c9be669874efca21e700638db160
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BestHTTP.Extensions
{
/// <summary>
/// Used in string parsers. Its Value is optional.
/// </summary>
public sealed class HeaderValue
{
#region Public Properties
public string Key { get; set; }
public string Value { get; set; }
public List<HeaderValue> Options { get; set; }
public bool HasValue { get { return !string.IsNullOrEmpty(this.Value); } }
#endregion
#region Constructors
public HeaderValue()
{ }
public HeaderValue(string key)
{
this.Key = key;
}
#endregion
#region Public Helper Functions
public void Parse(string headerStr, ref int pos)
{
ParseImplementation(headerStr, ref pos, true);
}
public bool TryGetOption(string key, out HeaderValue option)
{
option = null;
if (Options == null || Options.Count == 0)
return false;
for (int i = 0; i < Options.Count; ++i)
if (String.Equals(Options[i].Key, key, StringComparison.OrdinalIgnoreCase))
{
option = Options[i];
return true;
}
return false;
}
#endregion
#region Private Helper Functions
private void ParseImplementation(string headerStr, ref int pos, bool isOptionIsAnOption)
{
string key = headerStr.Read(ref pos, (ch) => ch != ';' && ch != '=' && ch != ',', true);
this.Key = key;
char? skippedChar = headerStr.Peek(pos - 1);
bool isValue = skippedChar == '=';
bool isOption = isOptionIsAnOption && skippedChar == ';';
while (skippedChar != null && isValue || isOption)
{
if (isValue)
{
string value = headerStr.ReadPossibleQuotedText(ref pos);
this.Value = value;
}
else if (isOption)
{
HeaderValue option = new HeaderValue();
option.ParseImplementation(headerStr, ref pos, false);
if (this.Options == null)
this.Options = new List<HeaderValue>();
this.Options.Add(option);
}
if (!isOptionIsAnOption)
return;
skippedChar = headerStr.Peek(pos - 1);
isValue = skippedChar == '=';
isOption = isOptionIsAnOption && skippedChar == ';';
}
}
#endregion
#region Overrides
public override string ToString()
{
if (!string.IsNullOrEmpty(Value))
return String.Concat(Key, '=', Value);
else
return Key;
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6e602b94f16de4df0bce5429a7b9f4c8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BestHTTP.Extensions
{
public interface IHeartbeat
{
void OnHeartbeatUpdate(TimeSpan dif);
}
/// <summary>
/// A manager class that can handle subscribing and unsubscribeing in the same update.
/// </summary>
public sealed class HeartbeatManager
{
private List<IHeartbeat> Heartbeats = new List<IHeartbeat>();
private IHeartbeat[] UpdateArray;
private DateTime LastUpdate = DateTime.MinValue;
public void Subscribe(IHeartbeat heartbeat)
{
lock (Heartbeats)
{
if (!Heartbeats.Contains(heartbeat))
Heartbeats.Add(heartbeat);
}
}
public void Unsubscribe(IHeartbeat heartbeat)
{
lock (Heartbeats)
Heartbeats.Remove(heartbeat);
}
public void Update()
{
if (LastUpdate == DateTime.MinValue)
LastUpdate = DateTime.UtcNow;
else
{
TimeSpan dif = DateTime.UtcNow - LastUpdate;
LastUpdate = DateTime.UtcNow;
int count = 0;
lock (Heartbeats)
{
if (UpdateArray == null || UpdateArray.Length < Heartbeats.Count)
Array.Resize(ref UpdateArray, Heartbeats.Count);
Heartbeats.CopyTo(0, UpdateArray, 0, Heartbeats.Count);
count = Heartbeats.Count;
}
for (int i = 0; i < count; ++i)
{
try
{
UpdateArray[i].OnHeartbeatUpdate(dif);
}
catch
{ }
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0a53e417bb6234382931cd702656827b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BestHTTP.Extensions
{
/// <summary>
/// Base class for specialized parsers
/// </summary>
public class KeyValuePairList
{
public List<HeaderValue> Values { get; protected set; }
public bool TryGet(string valueKeyName, out HeaderValue @param)
{
@param = null;
for (int i = 0; i < Values.Count; ++i)
if (string.CompareOrdinal(Values[i].Key, valueKeyName) == 0)
{
@param = Values[i];
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 385306b42b07044c885d807618235fe9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BestHTTP.Extensions
{
/// <summary>
/// Used for parsing WWW-Authenticate headers:
/// Digest realm="my realm", nonce="4664b327a2963503ba58bbe13ad672c0", qop=auth, opaque="f7e38bdc1c66fce214f9019ffe43117c"
/// </summary>
public sealed class WWWAuthenticateHeaderParser : KeyValuePairList
{
public WWWAuthenticateHeaderParser(string headerValue)
{
Values = ParseQuotedHeader(headerValue);
}
private List<HeaderValue> ParseQuotedHeader(string str)
{
List<HeaderValue> result = new List<HeaderValue>();
if (str != null)
{
int idx = 0;
// Read Type (Basic|Digest)
string type = str.Read(ref idx, (ch) => !char.IsWhiteSpace(ch) && !char.IsControl(ch)).TrimAndLower();
result.Add(new HeaderValue(type));
// process the rest of the text
while (idx < str.Length)
{
// Read key
string key = str.Read(ref idx, '=').TrimAndLower();
HeaderValue qp = new HeaderValue(key);
// Skip any white space
str.SkipWhiteSpace(ref idx);
qp.Value = str.ReadPossibleQuotedText(ref idx);
result.Add(qp);
}
}
return result;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 413bc04e0adfd469bbea744b57a8dd4c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9ce20480c77fb49ada91ab0e6863ef30
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BestHTTP.Forms
{
/// <summary>
/// This class represents a HTTP Form's field.
/// </summary>
public class HTTPFieldData
{
/// <summary>
/// The form's field.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Filename of the field. Optional.
/// </summary>
public string FileName { get; set; }
/// <summary>
/// Mime-type of the field. Optional
/// </summary>
public string MimeType { get; set; }
/// <summary>
/// Encoding of the data. Optional
/// </summary>
public Encoding Encoding { get; set; }
/// <summary>
/// The field's textual data.
/// </summary>
public string Text { get; set; }
/// <summary>
/// The field's binary data.
/// </summary>
public byte[] Binary { get; set; }
/// <summary>
/// Will return with the binary data, or if it's not present the textual data will be decoded to binary.
/// </summary>
public byte[] Payload
{
get
{
if (Binary != null)
return Binary;
if (Encoding == null)
Encoding = Encoding.UTF8;
return Binary = Encoding.GetBytes(Text);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7ccf16c6a45074f289839d19bf770289
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BestHTTP.Forms
{
/// <summary>
/// Base class of a concrete implementation. Don't use it to create a form, use instead one of the already wrote implementation(HTTPMultiPartForm, HTTPUrlEncodedForm), or create a new one by inheriting from this base class.
/// </summary>
public class HTTPFormBase
{
const int LongLength = 256;
#region Properties
/// <summary>
/// A list that holds the form's fields.
/// </summary>
public List<HTTPFieldData> Fields { get; set; }
/// <summary>
/// Returns true if the Fields has no element.
/// </summary>
public bool IsEmpty { get { return Fields == null || Fields.Count == 0; } }
/// <summary>
/// True if new fields has been added to our list.
/// </summary>
public bool IsChanged { get; protected set; }
/// <summary>
/// True if there are at least one form-field with binary data.
/// </summary>
public bool HasBinary { get; protected set; }
/// <summary>
/// True if there are at least one form-field with a long textual data.
/// </summary>
public bool HasLongValue { get; protected set; }
#endregion
#region Field Management
public void AddBinaryData(string fieldName, byte[] content)
{
AddBinaryData(fieldName, content, null, null);
}
public void AddBinaryData(string fieldName, byte[] content, string fileName)
{
AddBinaryData(fieldName, content, fileName, null);
}
public void AddBinaryData(string fieldName, byte[] content, string fileName, string mimeType)
{
if (Fields == null)
Fields = new List<HTTPFieldData>();
HTTPFieldData field = new HTTPFieldData();
field.Name = fieldName;
if (fileName == null)
field.FileName = fieldName + ".dat";
else
field.FileName = fileName;
if (mimeType == null)
field.MimeType = "application/octet-stream";
else
field.MimeType = mimeType;
field.Binary = content;
Fields.Add(field);
HasBinary = IsChanged = true;
}
public void AddField(string fieldName, string value)
{
AddField(fieldName, value, System.Text.Encoding.UTF8);
}
public void AddField(string fieldName, string value, System.Text.Encoding e)
{
if (Fields == null)
Fields = new List<HTTPFieldData>();
HTTPFieldData field = new HTTPFieldData();
field.Name = fieldName;
field.FileName = null;
if (e != null)
field.MimeType = "text/plain; charset=\"" + e.WebName + "\"";
field.Text = value;
field.Encoding = e;
Fields.Add(field);
IsChanged = true;
HasLongValue |= value.Length > LongLength;
}
#endregion
#region Virtual Functions
/// <summary>
/// It should 'clone' all the data from the given HTTPFormBase object.
/// Called after the form-implementation created.
/// </summary>
public virtual void CopyFrom(HTTPFormBase fields)
{
this.Fields = new List<HTTPFieldData>(fields.Fields);
this.IsChanged = true;
this.HasBinary = fields.HasBinary;
this.HasLongValue = fields.HasLongValue;
}
/// <summary>
/// Prepares the request to sending a form. It should set only the headers.
/// </summary>
public virtual void PrepareRequest(HTTPRequest request)
{
throw new NotImplementedException();
}
/// <summary>
/// Prepares and returns with the form's raw data.
/// </summary>
public virtual byte[] GetData()
{
throw new NotImplementedException();
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e53224c827a7d456fb88488b1981a8d9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BestHTTP.Forms
{
public enum HTTPFormUsage
{
/// <summary>
/// The plugin will try to choose the best form sending method.
/// </summary>
Automatic,
/// <summary>
/// The plugin will use the Url-Encoded form sending.
/// </summary>
UrlEncoded,
/// <summary>
/// The plugin will use the Multipart form sending.
/// </summary>
Multipart,
#if !BESTHTTP_DISABLE_UNITY_FORM
/// <summary>
/// The legacy, Unity-based form sending.
/// </summary>
Unity
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d4dbcc2441f514917998d3c7674f4d4c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 938113fbaaf4d4702a76b03aadd511c3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,79 @@
using BestHTTP.Extensions;
namespace BestHTTP.Forms
{
/// <summary>
/// A HTTP Form implementation to send textual and binary values.
/// </summary>
public sealed class HTTPMultiPartForm : HTTPFormBase
{
#region Private Fields
/// <summary>
/// A random boundary generated in the constructor.
/// </summary>
private string Boundary;
/// <summary>
///
/// </summary>
private byte[] CachedData;
#endregion
public HTTPMultiPartForm()
{
this.Boundary = this.GetHashCode().ToString("X");
}
#region IHTTPForm Implementation
public override void PrepareRequest(HTTPRequest request)
{
// Set up Content-Type header for the request
request.SetHeader("Content-Type", "multipart/form-data; boundary=\"" + Boundary + "\"");
}
public override byte[] GetData()
{
if (CachedData != null)
return CachedData;
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
for (int i = 0; i < Fields.Count; ++i)
{
HTTPFieldData field = Fields[i];
// Set the boundary
ms.WriteLine("--" + Boundary);
// Set up Content-Disposition header to our form with the name
ms.WriteLine("Content-Disposition: form-data; name=\"" + field.Name + "\"" + (!string.IsNullOrEmpty(field.FileName) ? "; filename=\"" + field.FileName + "\"" : string.Empty));
// Set up Content-Type head for the form.
if (!string.IsNullOrEmpty(field.MimeType))
ms.WriteLine("Content-Type: " + field.MimeType);
ms.WriteLine("Content-Length: " + field.Payload.Length.ToString());
ms.WriteLine();
// Write the actual data to the MemoryStream
ms.Write(field.Payload, 0, field.Payload.Length);
ms.Write(HTTPRequest.EOL, 0, HTTPRequest.EOL.Length);
}
// Write out the trailing boundary
ms.WriteLine("--" + Boundary + "--");
IsChanged = false;
// Set the RawData of our request
return CachedData = ms.ToArray();
}
}
#endregion
};
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b3a289c2138544bb4b2da0718726118e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BestHTTP.Forms
{
/// <summary>
/// A HTTP Form implementation to send textual values.
/// </summary>
public sealed class HTTPUrlEncodedForm : HTTPFormBase
{
private const int EscapeTreshold = 256;
private byte[] CachedData;
public override void PrepareRequest(HTTPRequest request)
{
request.SetHeader("Content-Type", "application/x-www-form-urlencoded");
}
public override byte[] GetData()
{
if (CachedData != null && !IsChanged)
return CachedData;
StringBuilder sb = new StringBuilder();
// Create a "field1=value1&field2=value2" formatted string
for (int i = 0; i < Fields.Count; ++i)
{
var field = Fields[i];
if (i > 0)
sb.Append("&");
sb.Append(EscapeString(field.Name));
sb.Append("=");
if (!string.IsNullOrEmpty(field.Text) || field.Binary == null)
sb.Append(EscapeString(field.Text));
else
// If forced to this form type with binary data, we will create a string from the binary data first and encode this string.
sb.Append(EscapeString(Encoding.UTF8.GetString(field.Binary, 0, field.Binary.Length)));
}
IsChanged = false;
return CachedData = Encoding.UTF8.GetBytes(sb.ToString());
}
public static string EscapeString(string originalString)
{
if (originalString.Length < EscapeTreshold)
return Uri.EscapeDataString(originalString);
else
{
int loops = originalString.Length / EscapeTreshold;
StringBuilder sb = new StringBuilder(loops);
for (int i = 0; i <= loops; i++)
sb.Append(i < loops ?
Uri.EscapeDataString(originalString.Substring(EscapeTreshold * i, EscapeTreshold)) :
Uri.EscapeDataString(originalString.Substring(EscapeTreshold * i)));
return sb.ToString();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 59a3665fc7cb0458e9af5eaa133b0dbe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,58 @@
#if !BESTHTTP_DISABLE_UNITY_FORM
using UnityEngine;
namespace BestHTTP.Forms
{
/// <summary>
/// For backward compatibility.
/// </summary>
public sealed class UnityForm : HTTPFormBase
{
public WWWForm Form { get; set; }
public UnityForm()
{
}
public UnityForm(WWWForm form)
{
Form = form;
}
public override void CopyFrom(HTTPFormBase fields)
{
this.Fields = fields.Fields;
this.IsChanged = true;
if (Form == null)
{
Form = new WWWForm();
if (Fields != null)
for (int i = 0; i < Fields.Count; ++i)
{
var field = Fields[i];
if (string.IsNullOrEmpty(field.Text) && field.Binary != null)
Form.AddBinaryData(field.Name, field.Binary, field.FileName, field.MimeType);
else
Form.AddField(field.Name, field.Text, field.Encoding);
}
}
}
public override void PrepareRequest(HTTPRequest request)
{
if (Form.headers.ContainsKey("Content-Type"))
request.SetHeader("Content-Type", Form.headers["Content-Type"] as string);
else
request.SetHeader("Content-Type", "application/x-www-form-urlencoded");
}
public override byte[] GetData()
{
return Form.data;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4453cf1b8d47f43799986c7c32b426a0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,825 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using BestHTTP.Extensions;
using BestHTTP.Authentication;
#if (!NETFX_CORE && !UNITY_WP8) || UNITY_EDITOR
using System.Net.Security;
#endif
#if !BESTHTTP_DISABLE_CACHING && (!UNITY_WEBGL || UNITY_EDITOR)
using BestHTTP.Caching;
#endif
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
using Org.BouncyCastle.Crypto.Tls;
#endif
#if !BESTHTTP_DISABLE_COOKIES && (!UNITY_WEBGL || UNITY_EDITOR)
using BestHTTP.Cookies;
#endif
#if NETFX_CORE || BUILD_FOR_WP8
using System.Threading.Tasks;
using Windows.Networking.Sockets;
using TcpClient = BestHTTP.PlatformSupport.TcpClient.WinRT.TcpClient;
//Disable CD4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
#pragma warning disable 4014
#elif UNITY_WP8 && !UNITY_EDITOR
using TcpClient = BestHTTP.PlatformSupport.TcpClient.WP8.TcpClient;
#else
using TcpClient = BestHTTP.PlatformSupport.TcpClient.General.TcpClient;
#endif
namespace BestHTTP
{
/// <summary>
/// https://tools.ietf.org/html/draft-thomson-hybi-http-timeout-03
/// Test servers: http://tools.ietf.org/ http://nginx.org/
/// </summary>
internal sealed class KeepAliveHeader
{
/// <summary>
/// A host sets the value of the "timeout" parameter to the time that the host will allow an idle connection to remain open before it is closed. A connection is idle if no data is sent or received by a host.
/// </summary>
public TimeSpan TimeOut { get; private set; }
/// <summary>
/// The "max" parameter has been used to indicate the maximum number of requests that would be made on the connection.This parameter is deprecated.Any limit on requests can be enforced by sending "Connection: close" and closing the connection.
/// </summary>
public int MaxRequests { get; private set; }
public void Parse(List<string> headerValues)
{
HeaderParser parser = new HeaderParser(headerValues[0]);
HeaderValue value;
if (parser.TryGet("timeout", out value) && value.HasValue)
{
int intValue = 0;
if (int.TryParse(value.Value, out intValue))
this.TimeOut = TimeSpan.FromSeconds(intValue);
else
this.TimeOut = TimeSpan.MaxValue;
}
if (parser.TryGet("max", out value) && value.HasValue)
{
int intValue = 0;
if (int.TryParse("max", out intValue))
this.MaxRequests = intValue;
else
this.MaxRequests = int.MaxValue;
}
}
}
internal enum RetryCauses
{
/// <summary>
/// The request processed without any special case.
/// </summary>
None,
/// <summary>
/// If the server closed the connection while we sending a request we should reconnect and send the request again. But we will try it once.
/// </summary>
Reconnect,
/// <summary>
/// We need an another try with Authorization header set.
/// </summary>
Authenticate,
#if !BESTHTTP_DISABLE_PROXY
/// <summary>
/// The proxy needs authentication.
/// </summary>
ProxyAuthenticate,
#endif
}
/// <summary>
/// Represents and manages a connection to a server.
/// </summary>
internal sealed class HTTPConnection : ConnectionBase
{
public override bool IsRemovable
{
get
{
// Plugin's own connection pooling
if (base.IsRemovable)
return true;
// Overridden keep-alive timeout by a Keep-Alive header
if (IsFree && KeepAlive != null && (DateTime.UtcNow - base.LastProcessTime) >= KeepAlive.TimeOut)
return true;
return false;
}
}
#region Private Properties
private TcpClient Client;
private Stream Stream;
private KeepAliveHeader KeepAlive;
#endregion
internal HTTPConnection(string serverAddress)
:base(serverAddress)
{}
#region Request Processing Implementation
protected override
#if NETFX_CORE
async
#endif
void ThreadFunc(object param)
{
bool alreadyReconnected = false;
bool redirected = false;
RetryCauses cause = RetryCauses.None;
try
{
#if !BESTHTTP_DISABLE_PROXY
if (!HasProxy && CurrentRequest.HasProxy)
Proxy = CurrentRequest.Proxy;
#endif
#if !BESTHTTP_DISABLE_CACHING && (!UNITY_WEBGL || UNITY_EDITOR)
// Try load the full response from an already saved cache entity. If the response
if (TryLoadAllFromCache())
return;
#endif
if (Client != null && !Client.IsConnected())
Close();
do // of while (reconnect)
{
if (cause == RetryCauses.Reconnect)
{
Close();
#if NETFX_CORE
await Task.Delay(100);
#else
Thread.Sleep(100);
#endif
}
LastProcessedUri = CurrentRequest.CurrentUri;
cause = RetryCauses.None;
// Connect to the server
Connect();
if (State == HTTPConnectionStates.AbortRequested)
throw new Exception("AbortRequested");
#if !BESTHTTP_DISABLE_CACHING && (!UNITY_WEBGL || UNITY_EDITOR)
// Setup cache control headers before we send out the request
if (!CurrentRequest.DisableCache)
HTTPCacheService.SetHeaders(CurrentRequest);
#endif
// Write the request to the stream
// sentRequest will be true if the request sent out successfully(no SocketException), so we can try read the response
bool sentRequest = false;
try
{
CurrentRequest.SendOutTo(Stream);
sentRequest = true;
}
catch (Exception ex)
{
Close();
if (State == HTTPConnectionStates.TimedOut ||
State == HTTPConnectionStates.AbortRequested)
throw new Exception("AbortRequested");
// We will try again only once
if (!alreadyReconnected && !CurrentRequest.DisableRetry)
{
alreadyReconnected = true;
cause = RetryCauses.Reconnect;
}
else // rethrow exception
throw ex;
}
// If sending out the request succeeded, we will try read the response.
if (sentRequest)
{
bool received = Receive();
if (State == HTTPConnectionStates.TimedOut ||
State == HTTPConnectionStates.AbortRequested)
throw new Exception("AbortRequested");
if (!received && !alreadyReconnected && !CurrentRequest.DisableRetry)
{
alreadyReconnected = true;
cause = RetryCauses.Reconnect;
}
if (CurrentRequest.Response != null)
{
#if !BESTHTTP_DISABLE_COOKIES && (!UNITY_WEBGL || UNITY_EDITOR)
// Try to store cookies before we do anything else, as we may remove the response deleting the cookies as well.
if (CurrentRequest.IsCookiesEnabled)
CookieJar.Set(CurrentRequest.Response);
#endif
switch (CurrentRequest.Response.StatusCode)
{
// Not authorized
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2
case 401:
{
string authHeader = DigestStore.FindBest(CurrentRequest.Response.GetHeaderValues("www-authenticate"));
if (!string.IsNullOrEmpty(authHeader))
{
var digest = DigestStore.GetOrCreate(CurrentRequest.CurrentUri);
digest.ParseChallange(authHeader);
if (CurrentRequest.Credentials != null && digest.IsUriProtected(CurrentRequest.CurrentUri) && (!CurrentRequest.HasHeader("Authorization") || digest.Stale))
cause = RetryCauses.Authenticate;
}
goto default;
}
#if !BESTHTTP_DISABLE_PROXY
// Proxy authentication required
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.8
case 407:
{
if (CurrentRequest.HasProxy)
{
string authHeader = DigestStore.FindBest(CurrentRequest.Response.GetHeaderValues("proxy-authenticate"));
if (!string.IsNullOrEmpty(authHeader))
{
var digest = DigestStore.GetOrCreate(CurrentRequest.Proxy.Address);
digest.ParseChallange(authHeader);
if (CurrentRequest.Proxy.Credentials != null && digest.IsUriProtected(CurrentRequest.Proxy.Address) && (!CurrentRequest.HasHeader("Proxy-Authorization") || digest.Stale))
cause = RetryCauses.ProxyAuthenticate;
}
}
goto default;
}
#endif
// Redirected
case 301: // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.2
case 302: // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.3
case 307: // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.8
case 308: // http://tools.ietf.org/html/rfc7238
{
if (CurrentRequest.RedirectCount >= CurrentRequest.MaxRedirects)
goto default;
CurrentRequest.RedirectCount++;
string location = CurrentRequest.Response.GetFirstHeaderValue("location");
if (!string.IsNullOrEmpty(location))
{
if (HTTPManager.Logger.Level == Logger.Loglevels.All)
HTTPManager.Logger.Verbose("HTTPConnection", string.Format("{0} - Redirected to {1}", this.CurrentRequest.CurrentUri.ToString(), location));
Uri redirectUri = GetRedirectUri(location);
// Let the user to take some control over the redirection
if (!CurrentRequest.CallOnBeforeRedirection(redirectUri))
{
HTTPManager.Logger.Information("HTTPConnection", "OnBeforeRedirection returned False");
goto default;
}
// Remove the previously set Host header.
CurrentRequest.RemoveHeader("Host");
// Set the Referer header to the last Uri.
CurrentRequest.SetHeader("Referer", CurrentRequest.CurrentUri.ToString());
// Set the new Uri, the CurrentUri will return this while the IsRedirected property is true
CurrentRequest.RedirectUri = redirectUri;
// Discard the redirect response, we don't need it any more
CurrentRequest.Response = null;
redirected = CurrentRequest.IsRedirected = true;
}
else
#if !NETFX_CORE
throw new MissingFieldException(string.Format("Got redirect status({0}) without 'location' header!", CurrentRequest.Response.StatusCode.ToString()));
#else
throw new Exception(string.Format("Got redirect status({0}) without 'location' header!", CurrentRequest.Response.StatusCode.ToString()));
#endif
goto default;
}
default:
#if !BESTHTTP_DISABLE_CACHING && (!UNITY_WEBGL || UNITY_EDITOR)
TryStoreInCache();
#endif
break;
}
// If we have a response and the server telling us that it closed the connection after the message sent to us, then
// we will close the connection too.
if (!CurrentRequest.IsKeepAlive ||
CurrentRequest.Response == null ||
(!CurrentRequest.Response.IsClosedManually && CurrentRequest.Response.HasHeaderWithValue("connection", "close")))
Close();
else if (CurrentRequest.Response != null)
{
var keepAliveheaderValues = CurrentRequest.Response.GetHeaderValues("keep-alive");
if (keepAliveheaderValues != null && keepAliveheaderValues.Count > 0)
{
if (KeepAlive == null)
KeepAlive = new KeepAliveHeader();
KeepAlive.Parse(keepAliveheaderValues);
}
}
}
}
} while (cause != RetryCauses.None);
}
catch(TimeoutException e)
{
CurrentRequest.Response = null;
CurrentRequest.Exception = e;
CurrentRequest.State = HTTPRequestStates.ConnectionTimedOut;
Close();
}
catch (Exception e)
{
if (CurrentRequest != null)
{
#if !BESTHTTP_DISABLE_CACHING && (!UNITY_WEBGL || UNITY_EDITOR)
if (CurrentRequest.UseStreaming)
HTTPCacheService.DeleteEntity(CurrentRequest.CurrentUri);
#endif
// Something gone bad, Response must be null!
CurrentRequest.Response = null;
switch (State)
{
case HTTPConnectionStates.Closed:
case HTTPConnectionStates.AbortRequested:
CurrentRequest.State = HTTPRequestStates.Aborted;
break;
case HTTPConnectionStates.TimedOut:
CurrentRequest.State = HTTPRequestStates.TimedOut;
break;
default:
CurrentRequest.Exception = e;
CurrentRequest.State = HTTPRequestStates.Error;
break;
}
}
Close();
}
finally
{
if (CurrentRequest != null)
{
// Avoid state changes. While we are in this block changing the connection's State, on Unity's main thread
// the HTTPManager's OnUpdate will check the connections's State and call functions that can change the inner state of
// the object. (Like setting the CurrentRequest to null in function Recycle() causing a NullRef exception)
lock (HTTPManager.Locker)
{
if (CurrentRequest != null && CurrentRequest.Response != null && CurrentRequest.Response.IsUpgraded)
State = HTTPConnectionStates.Upgraded;
else
State = redirected ? HTTPConnectionStates.Redirected : (Client == null ? HTTPConnectionStates.Closed : HTTPConnectionStates.WaitForRecycle);
// Change the request's state only when the whole processing finished
if (CurrentRequest.State == HTTPRequestStates.Processing && (State == HTTPConnectionStates.Closed || State == HTTPConnectionStates.WaitForRecycle))
{
if (CurrentRequest.Response != null)
CurrentRequest.State = HTTPRequestStates.Finished;
else
{
CurrentRequest.Exception = new Exception(string.Format("Remote server closed the connection before sending response header! Previous request state: {0}. Connection state: {1}",
CurrentRequest.State.ToString(),
State.ToString()));
CurrentRequest.State = HTTPRequestStates.Error;
}
}
if (CurrentRequest.State == HTTPRequestStates.ConnectionTimedOut)
State = HTTPConnectionStates.Closed;
LastProcessTime = DateTime.UtcNow;
if (OnConnectionRecycled != null)
RecycleNow();
}
#if !BESTHTTP_DISABLE_CACHING && (!UNITY_WEBGL || UNITY_EDITOR)
HTTPCacheService.SaveLibrary();
#endif
#if !BESTHTTP_DISABLE_COOKIES && (!UNITY_WEBGL || UNITY_EDITOR)
CookieJar.Persist();
#endif
}
}
}
private void Connect()
{
Uri uri =
#if !BESTHTTP_DISABLE_PROXY
CurrentRequest.HasProxy ? CurrentRequest.Proxy.Address :
#endif
CurrentRequest.CurrentUri;
#region TCP Connection
if (Client == null)
Client = new TcpClient();
if (!Client.Connected)
{
Client.ConnectTimeout = CurrentRequest.ConnectTimeout;
#if NETFX_CORE || (UNITY_WP8 && !UNITY_EDITOR)
Client.UseHTTPSProtocol =
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
!CurrentRequest.UseAlternateSSL &&
#endif
HTTPProtocolFactory.IsSecureProtocol(uri);
#endif
if (HTTPManager.Logger.Level == Logger.Loglevels.All)
HTTPManager.Logger.Verbose("HTTPConnection", string.Format("'{0}' - Connecting to {1}:{2}", this.CurrentRequest.CurrentUri.ToString(), uri.Host, uri.Port.ToString()));
Client.Connect(uri.Host, uri.Port);
if (HTTPManager.Logger.Level <= Logger.Loglevels.Information)
HTTPManager.Logger.Information("HTTPConnection", "Connected to " + uri.Host + ":" + uri.Port.ToString());
}
else if (HTTPManager.Logger.Level <= Logger.Loglevels.Information)
HTTPManager.Logger.Information("HTTPConnection", "Already connected to " + uri.Host + ":" + uri.Port.ToString());
#endregion
StartTime = DateTime.UtcNow;
if (Stream == null)
{
bool isSecure = HTTPProtocolFactory.IsSecureProtocol(CurrentRequest.CurrentUri);
Stream = Client.GetStream();
/*if (Stream.CanTimeout)
Stream.ReadTimeout = Stream.WriteTimeout = (int)CurrentRequest.Timeout.TotalMilliseconds;*/
#if !BESTHTTP_DISABLE_PROXY
#region Proxy Handling
if (HasProxy && (!Proxy.IsTransparent || (isSecure && Proxy.NonTransparentForHTTPS)))
{
var outStream = new BinaryWriter(Stream);
bool retry;
do
{
// If we have to because of a authentication request, we will switch it to true
retry = false;
string connectStr = string.Format("CONNECT {0}:{1} HTTP/1.1", CurrentRequest.CurrentUri.Host, CurrentRequest.CurrentUri.Port);
HTTPManager.Logger.Information("HTTPConnection", "Sending " + connectStr);
outStream.SendAsASCII(connectStr);
outStream.Write(HTTPRequest.EOL);
outStream.SendAsASCII("Proxy-Connection: Keep-Alive");
outStream.Write(HTTPRequest.EOL);
outStream.SendAsASCII("Connection: Keep-Alive");
outStream.Write(HTTPRequest.EOL);
outStream.SendAsASCII(string.Format("Host: {0}:{1}", CurrentRequest.CurrentUri.Host, CurrentRequest.CurrentUri.Port));
outStream.Write(HTTPRequest.EOL);
// Proxy Authentication
if (HasProxy && Proxy.Credentials != null)
{
switch (Proxy.Credentials.Type)
{
case AuthenticationTypes.Basic:
// With Basic authentication we don't want to wait for a challenge, we will send the hash with the first request
outStream.Write(string.Format("Proxy-Authorization: {0}", string.Concat("Basic ", Convert.ToBase64String(Encoding.UTF8.GetBytes(Proxy.Credentials.UserName + ":" + Proxy.Credentials.Password)))).GetASCIIBytes());
outStream.Write(HTTPRequest.EOL);
break;
case AuthenticationTypes.Unknown:
case AuthenticationTypes.Digest:
var digest = DigestStore.Get(Proxy.Address);
if (digest != null)
{
string authentication = digest.GenerateResponseHeader(CurrentRequest, Proxy.Credentials);
if (!string.IsNullOrEmpty(authentication))
{
outStream.Write(string.Format("Proxy-Authorization: {0}", authentication).GetASCIIBytes());
outStream.Write(HTTPRequest.EOL);
}
}
break;
}
}
outStream.Write(HTTPRequest.EOL);
// Make sure to send all the wrote data to the wire
outStream.Flush();
CurrentRequest.ProxyResponse = new HTTPResponse(CurrentRequest, Stream, false, false);
// Read back the response of the proxy
if (!CurrentRequest.ProxyResponse.Receive(-1, true))
throw new Exception("Connection to the Proxy Server failed!");
if (HTTPManager.Logger.Level <= Logger.Loglevels.Information)
HTTPManager.Logger.Information("HTTPConnection", "Proxy returned - status code: " + CurrentRequest.ProxyResponse.StatusCode + " message: " + CurrentRequest.ProxyResponse.Message);
switch(CurrentRequest.ProxyResponse.StatusCode)
{
// Proxy authentication required
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.8
case 407:
{
string authHeader = DigestStore.FindBest(CurrentRequest.ProxyResponse.GetHeaderValues("proxy-authenticate"));
if (!string.IsNullOrEmpty(authHeader))
{
var digest = DigestStore.GetOrCreate(Proxy.Address);
digest.ParseChallange(authHeader);
if (Proxy.Credentials != null && digest.IsUriProtected(Proxy.Address) && (!CurrentRequest.HasHeader("Proxy-Authorization") || digest.Stale))
retry = true;
}
break;
}
default:
if (!CurrentRequest.ProxyResponse.IsSuccess)
throw new Exception(string.Format("Proxy returned Status Code: \"{0}\", Message: \"{1}\" and Response: {2}", CurrentRequest.ProxyResponse.StatusCode, CurrentRequest.ProxyResponse.Message, CurrentRequest.ProxyResponse.DataAsText));
break;
}
} while (retry);
}
#endregion
#endif // #if !BESTHTTP_DISABLE_PROXY
// We have to use CurrentRequest.CurrentUri here, because uri can be a proxy uri with a different protocol
if (isSecure)
{
#region SSL Upgrade
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
if (CurrentRequest.UseAlternateSSL)
{
var handler = new TlsClientProtocol(Client.GetStream(), new Org.BouncyCastle.Security.SecureRandom());
// http://tools.ietf.org/html/rfc3546#section-3.1
// It is RECOMMENDED that clients include an extension of type "server_name" in the client hello whenever they locate a server by a supported name type.
List<string> hostNames = new List<string>(1);
hostNames.Add(CurrentRequest.CurrentUri.Host);
handler.Connect(new LegacyTlsClient(CurrentRequest.CurrentUri,
CurrentRequest.CustomCertificateVerifyer == null ? new AlwaysValidVerifyer() : CurrentRequest.CustomCertificateVerifyer,
CurrentRequest.CustomClientCredentialsProvider,
hostNames));
Stream = handler.Stream;
}
else
#endif
{
#if !NETFX_CORE && !UNITY_WP8
SslStream sslStream = new SslStream(Client.GetStream(), false, (sender, cert, chain, errors) =>
{
return CurrentRequest.CallCustomCertificationValidator(cert, chain);
});
if (!sslStream.IsAuthenticated)
sslStream.AuthenticateAsClient(CurrentRequest.CurrentUri.Host);
Stream = sslStream;
#else
Stream = Client.GetStream();
#endif
}
#endregion
}
}
}
private bool Receive()
{
SupportedProtocols protocol = CurrentRequest.ProtocolHandler == SupportedProtocols.Unknown ? HTTPProtocolFactory.GetProtocolFromUri(CurrentRequest.CurrentUri) : CurrentRequest.ProtocolHandler;
if (HTTPManager.Logger.Level == Logger.Loglevels.All)
HTTPManager.Logger.Verbose("HTTPConnection", string.Format("{0} - Receive - protocol: {1}", this.CurrentRequest.CurrentUri.ToString(), protocol.ToString()));
CurrentRequest.Response = HTTPProtocolFactory.Get(protocol, CurrentRequest, Stream, CurrentRequest.UseStreaming, false);
if (!CurrentRequest.Response.Receive())
{
if (HTTPManager.Logger.Level == Logger.Loglevels.All)
HTTPManager.Logger.Verbose("HTTPConnection", string.Format("{0} - Receive - Failed! Response will be null, returning with false.", this.CurrentRequest.CurrentUri.ToString()));
CurrentRequest.Response = null;
return false;
}
// We didn't check HTTPManager.IsCachingDisabled's value on purpose. (sending out a request with conditional get then change IsCachingDisabled to true may produce undefined behavior)
if (CurrentRequest.Response.StatusCode == 304)
{
#if !BESTHTTP_DISABLE_CACHING && (!UNITY_WEBGL || UNITY_EDITOR)
if (CurrentRequest.IsRedirected)
{
if (!LoadFromCache(CurrentRequest.RedirectUri))
LoadFromCache(CurrentRequest.Uri);
}
else
LoadFromCache(CurrentRequest.Uri);
#else
return false;
#endif
}
if (HTTPManager.Logger.Level == Logger.Loglevels.All)
HTTPManager.Logger.Verbose("HTTPConnection", string.Format("{0} - Receive - Finished Successfully!", this.CurrentRequest.CurrentUri.ToString()));
return true;
}
#endregion
#region Helper Functions
#if !BESTHTTP_DISABLE_CACHING && (!UNITY_WEBGL || UNITY_EDITOR)
private bool LoadFromCache(Uri uri)
{
if (HTTPManager.Logger.Level == Logger.Loglevels.All)
HTTPManager.Logger.Verbose("HTTPConnection", string.Format("{0} - LoadFromCache for Uri: {1}", this.CurrentRequest.CurrentUri.ToString(), uri.ToString()));
int bodyLength;
using (var cacheStream = HTTPCacheService.GetBody(uri, out bodyLength))
{
if (cacheStream == null)
return false;
if (!CurrentRequest.Response.HasHeader("content-length"))
CurrentRequest.Response.Headers.Add("content-length", new List<string>(1) { bodyLength.ToString() });
CurrentRequest.Response.IsFromCache = true;
CurrentRequest.Response.ReadRaw(cacheStream, bodyLength);
}
return true;
}
private bool TryLoadAllFromCache()
{
if (CurrentRequest.DisableCache || !HTTPCacheService.IsSupported)
return false;
// We will try read the response from the cache, but if something happens we will fallback to the normal way.
try
{
//Unless specifically constrained by a cache-control (section 14.9) directive, a caching system MAY always store a successful response (see section 13.8) as a cache entity,
// MAY return it without validation if it is fresh, and MAY return it after successful validation.
// MAY return it without validation if it is fresh!
if (HTTPCacheService.IsCachedEntityExpiresInTheFuture(CurrentRequest))
{
if (HTTPManager.Logger.Level == Logger.Loglevels.All)
HTTPManager.Logger.Verbose("HTTPConnection", string.Format("{0} - TryLoadAllFromCache - whole response loading from cache", this.CurrentRequest.CurrentUri.ToString()));
CurrentRequest.Response = HTTPCacheService.GetFullResponse(CurrentRequest);
if (CurrentRequest.Response != null)
return true;
}
}
catch
{
HTTPCacheService.DeleteEntity(CurrentRequest.CurrentUri);
}
return false;
}
#endif
#if !BESTHTTP_DISABLE_CACHING && (!UNITY_WEBGL || UNITY_EDITOR)
private void TryStoreInCache()
{
// if UseStreaming && !DisableCache then we already wrote the response to the cache
if (!CurrentRequest.UseStreaming &&
!CurrentRequest.DisableCache &&
CurrentRequest.Response != null &&
HTTPCacheService.IsSupported &&
HTTPCacheService.IsCacheble(CurrentRequest.CurrentUri, CurrentRequest.MethodType, CurrentRequest.Response))
{
if(CurrentRequest.IsRedirected)
HTTPCacheService.Store(CurrentRequest.Uri, CurrentRequest.MethodType, CurrentRequest.Response);
else
HTTPCacheService.Store(CurrentRequest.CurrentUri, CurrentRequest.MethodType, CurrentRequest.Response);
}
}
#endif
private Uri GetRedirectUri(string location)
{
Uri result = null;
try
{
result = new Uri(location);
}
#if !NETFX_CORE
catch (UriFormatException)
#else
catch
#endif
{
// Sometimes the server sends back only the path and query component of the new uri
var uri = CurrentRequest.Uri;
var builder = new UriBuilder(uri.Scheme, uri.Host, uri.Port, location);
result = builder.Uri;
}
return result;
}
internal override void Abort(HTTPConnectionStates newState)
{
State = newState;
switch(State)
{
case HTTPConnectionStates.TimedOut: TimedOutStart = DateTime.UtcNow; break;
}
if (Stream != null)
Stream.Dispose();
}
private void Close()
{
KeepAlive = null;
LastProcessedUri = null;
if (Client != null)
{
try
{
Client.Close();
}
catch
{
}
finally
{
Stream = null;
Client = null;
}
}
}
#endregion
protected override void Dispose(bool disposing)
{
Close();
base.Dispose(disposing);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e8576a2898989475d984ce722ee18fe9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,59 @@
namespace BestHTTP
{
/// <summary>
/// Possible states of a Http Connection.
/// The ideal lifecycle of a connection that has KeepAlive is the following: Initial => [Processing => WaitForRecycle => Free] => Closed.
/// </summary>
internal enum HTTPConnectionStates
{
/// <summary>
/// This Connection instance is just created.
/// </summary>
Initial,
/// <summary>
/// This Connection is processing a request
/// </summary>
Processing,
/// <summary>
/// The request redirected.
/// </summary>
Redirected,
/// <summary>
/// The connection is upgraded from http.
/// </summary>
Upgraded,
/// <summary>
/// Wait for the upgraded protocol to shut down.
/// </summary>
WaitForProtocolShutdown,
/// <summary>
/// The Connection is finished processing the request, it's waiting now to deliver it's result.
/// </summary>
WaitForRecycle,
/// <summary>
/// The request result's delivered, it's now up to processing again.
/// </summary>
Free,
/// <summary>
/// A request from outside of the plugin to abort the connection.
/// </summary>
AbortRequested,
/// <summary>
/// The request is not finished in the given time.
/// </summary>
TimedOut,
/// <summary>
/// If it's not a KeepAlive connection, or something happend, then we close this connection and remove from the pool.
/// </summary>
Closed
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 31b0ada5c3afb4813b1da7c89897011b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,768 @@
using System;
using System.Collections.Generic;
using UnityEngine;
#if !BESTHTTP_DISABLE_CACHING && (!UNITY_WEBGL || UNITY_EDITOR)
using BestHTTP.Caching;
#endif
using BestHTTP.Extensions;
using BestHTTP.Logger;
using BestHTTP.Statistics;
namespace BestHTTP
{
/// <summary>
///
/// </summary>
public static class HTTPManager
{
// Static constructor. Setup default values
static HTTPManager()
{
MaxConnectionPerServer = 4;
KeepAliveDefaultValue = true;
MaxPathLength = 255;
MaxConnectionIdleTime = TimeSpan.FromSeconds(20);
#if !BESTHTTP_DISABLE_COOKIES && (!UNITY_WEBGL || UNITY_EDITOR)
IsCookiesEnabled = true;
#endif
CookieJarSize = 10 * 1024 * 1024;
EnablePrivateBrowsing = false;
ConnectTimeout = TimeSpan.FromSeconds(20);
RequestTimeout = TimeSpan.FromSeconds(60);
// Set the default logger mechanism
logger = new BestHTTP.Logger.DefaultLogger();
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
DefaultCertificateVerifyer = null;
UseAlternateSSLDefaultValue = true;
#endif
}
#region Global Options
/// <summary>
/// The maximum active TCP connections that the client will maintain to a server. Default value is 4. Minimum value is 1.
/// </summary>
public static byte MaxConnectionPerServer
{
get{ return maxConnectionPerServer; }
set
{
if (value <= 0)
throw new ArgumentOutOfRangeException("MaxConnectionPerServer must be greater than 0!");
maxConnectionPerServer = value;
}
}
private static byte maxConnectionPerServer;
/// <summary>
/// Default value of a HTTP request's IsKeepAlive value. Default value is true. If you make rare request to the server it's should be changed to false.
/// </summary>
public static bool KeepAliveDefaultValue { get; set; }
#if !BESTHTTP_DISABLE_CACHING && (!UNITY_WEBGL || UNITY_EDITOR)
/// <summary>
/// Set to true, if caching is prohibited.
/// </summary>
public static bool IsCachingDisabled { get; set; }
#endif
/// <summary>
/// How many time must be passed to destroy that connection after a connection finished its last request. Its default value is 20 seconds.
/// </summary>
public static TimeSpan MaxConnectionIdleTime { get; set; }
#if !BESTHTTP_DISABLE_COOKIES && (!UNITY_WEBGL || UNITY_EDITOR)
/// <summary>
/// Set to false to disable all Cookie. It's default value is true.
/// </summary>
public static bool IsCookiesEnabled { get; set; }
#endif
/// <summary>
/// Size of the Cookie Jar in bytes. It's default value is 10485760 (10 MB).
/// </summary>
public static uint CookieJarSize { get; set; }
/// <summary>
/// If this property is set to true, then new cookies treated as session cookies and these cookies are not saved to disk. Its default value is false;
/// </summary>
public static bool EnablePrivateBrowsing { get; set; }
/// <summary>
/// Global, default value of the HTTPRequest's ConnectTimeout property. If set to TimeSpan.Zero or lower, no connect timeout logic is executed. Default value is 20 seconds.
/// </summary>
public static TimeSpan ConnectTimeout { get; set; }
/// <summary>
/// Global, default value of the HTTPRequest's Timeout property. Default value is 60 seconds.
/// </summary>
public static TimeSpan RequestTimeout { get; set; }
#if !((BESTHTTP_DISABLE_CACHING && BESTHTTP_DISABLE_COOKIES) || (UNITY_WEBGL && !UNITY_EDITOR))
/// <summary>
/// By default the plugin will save all cache and cookie data under the path returned by Application.persistentDataPath.
/// You can assign a function to this delegate to return a custom root path to define a new path.
/// <remarks>This delegate will be called on a non Unity thread!</remarks>
/// </summary>
public static System.Func<string> RootCacheFolderProvider { get; set; }
#endif
#if !BESTHTTP_DISABLE_PROXY
/// <summary>
/// The global, default proxy for all HTTPRequests. The HTTPRequest's Proxy still can be changed per-request. Default value is null.
/// </summary>
public static HTTPProxy Proxy { get; set; }
#endif
/// <summary>
/// Heartbeat manager to use less threads in the plugin. The heartbeat updates are called from the OnUpdate function.
/// </summary>
public static HeartbeatManager Heartbeats
{
get
{
if (heartbeats == null)
heartbeats = new HeartbeatManager();
return heartbeats;
}
}
private static HeartbeatManager heartbeats;
/// <summary>
/// A basic BestHTTP.Logger.ILogger implementation to be able to log intelligently additional informations about the plugin's internal mechanism.
/// </summary>
public static BestHTTP.Logger.ILogger Logger
{
get
{
// Make sure that it has a valid logger instance.
if (logger == null)
{
logger = new DefaultLogger();
logger.Level = Loglevels.None;
}
return logger;
}
set { logger = value; }
}
private static BestHTTP.Logger.ILogger logger;
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
/// <summary>
/// The default ICertificateVerifyer implementation that the plugin will use when the request's UseAlternateSSL property is set to true.
/// </summary>
public static Org.BouncyCastle.Crypto.Tls.ICertificateVerifyer DefaultCertificateVerifyer { get; set; }
/// <summary>
/// The default IClientCredentialsProvider implementation that the plugin will use when the request's UseAlternateSSL property is set to true.
/// </summary>
public static Org.BouncyCastle.Crypto.Tls.IClientCredentialsProvider DefaultClientCredentialsProvider { get; set; }
/// <summary>
/// The default value for the HTTPRequest's UseAlternateSSL property.
/// </summary>
public static bool UseAlternateSSLDefaultValue { get; set; }
#endif
/// <summary>
/// On most systems the maximum length of a path is around 255 character. If a cache entity's path is longer than this value it doesn't get cached. There no platform independent API to query the exact value on the current system, but it's
/// exposed here and can be overridden. It's default value is 255.
/// </summary>
internal static int MaxPathLength { get; set; }
#endregion
#region Manager variables
/// <summary>
/// All connection has a reference in this Dictionary untill it's removed completly.
/// </summary>
private static Dictionary<string, List<ConnectionBase>> Connections = new Dictionary<string, List<ConnectionBase>>();
/// <summary>
/// Active connections. These connections all has a request to process.
/// </summary>
private static List<ConnectionBase> ActiveConnections = new List<ConnectionBase>();
/// <summary>
/// Free connections. They can be removed completly after a specified time.
/// </summary>
private static List<ConnectionBase> FreeConnections = new List<ConnectionBase>();
/// <summary>
/// Connections that recycled in the Update loop. If they are not used in the same loop to process a request, they will be transferred to the FreeConnections list.
/// </summary>
private static List<ConnectionBase> RecycledConnections = new List<ConnectionBase>();
/// <summary>
/// List of request that have to wait until there is a free connection to the server.
/// </summary>
private static List<HTTPRequest> RequestQueue = new List<HTTPRequest>();
private static bool IsCallingCallbacks;
internal static System.Object Locker = new System.Object();
#endregion
#region Public Interface
public static void Setup()
{
HTTPUpdateDelegator.CheckInstance();
#if !BESTHTTP_DISABLE_CACHING && (!UNITY_WEBGL || UNITY_EDITOR)
HTTPCacheService.CheckSetup();
#endif
#if !BESTHTTP_DISABLE_COOKIES && (!UNITY_WEBGL || UNITY_EDITOR)
Cookies.CookieJar.SetupFolder();
#endif
}
public static HTTPRequest SendRequest(string url, OnRequestFinishedDelegate callback)
{
return SendRequest(new HTTPRequest(new Uri(url), HTTPMethods.Get, callback));
}
public static HTTPRequest SendRequest(string url, HTTPMethods methodType, OnRequestFinishedDelegate callback)
{
return SendRequest(new HTTPRequest(new Uri(url), methodType, callback));
}
public static HTTPRequest SendRequest(string url, HTTPMethods methodType, bool isKeepAlive, OnRequestFinishedDelegate callback)
{
return SendRequest(new HTTPRequest(new Uri(url), methodType, isKeepAlive, callback));
}
public static HTTPRequest SendRequest(string url, HTTPMethods methodType, bool isKeepAlive, bool disableCache, OnRequestFinishedDelegate callback)
{
return SendRequest(new HTTPRequest(new Uri(url), methodType, isKeepAlive, disableCache, callback));
}
public static HTTPRequest SendRequest(HTTPRequest request)
{
lock (Locker)
{
Setup();
if (IsCallingCallbacks)
{
request.State = HTTPRequestStates.Queued;
RequestQueue.Add(request);
}
else
SendRequestImpl(request);
return request;
}
}
public static GeneralStatistics GetGeneralStatistics(StatisticsQueryFlags queryFlags)
{
GeneralStatistics stat = new GeneralStatistics();
stat.QueryFlags = queryFlags;
if ((queryFlags & StatisticsQueryFlags.Connections) != 0)
{
int connections = 0;
foreach(var conn in HTTPManager.Connections)
{
if (conn.Value != null)
connections += conn.Value.Count;
}
#if !BESTHTTP_DISABLE_WEBSOCKET && UNITY_WEBGL && !UNITY_EDITOR
connections += WebSocket.WebSocket.WebSockets.Count;
#endif
stat.Connections = connections;
stat.ActiveConnections = ActiveConnections.Count
#if !BESTHTTP_DISABLE_WEBSOCKET && UNITY_WEBGL && !UNITY_EDITOR
+ WebSocket.WebSocket.WebSockets.Count
#endif
;
stat.FreeConnections = FreeConnections.Count;
stat.RecycledConnections = RecycledConnections.Count;
stat.RequestsInQueue = RequestQueue.Count;
}
#if !BESTHTTP_DISABLE_CACHING && (!UNITY_WEBGL || UNITY_EDITOR)
if ((queryFlags & StatisticsQueryFlags.Cache) != 0)
{
stat.CacheEntityCount = HTTPCacheService.GetCacheEntityCount();
stat.CacheSize = HTTPCacheService.GetCacheSize();
}
#endif
#if !BESTHTTP_DISABLE_COOKIES && (!UNITY_WEBGL || UNITY_EDITOR)
if ((queryFlags & StatisticsQueryFlags.Cookies) != 0)
{
List<Cookies.Cookie> cookies = Cookies.CookieJar.GetAll();
stat.CookieCount = cookies.Count;
uint cookiesSize = 0;
for (int i = 0; i < cookies.Count; ++i)
cookiesSize += cookies[i].GuessSize();
stat.CookieJarSize = cookiesSize;
}
#endif
return stat;
}
#endregion
#region Private Functions
private static void SendRequestImpl(HTTPRequest request)
{
ConnectionBase conn = FindOrCreateFreeConnection(request);
if (conn != null)
{
// found a free connection: put it in the ActiveConnection list(they will be checked periodically in the OnUpdate call)
if (ActiveConnections.Find((c) => c == conn) == null)
ActiveConnections.Add(conn);
FreeConnections.Remove(conn);
request.State = HTTPRequestStates.Processing;
request.Prepare();
// then start process the request
conn.Process(request);
}
else
{
// If no free connection found and creation prohibited, we will put back to the queue
request.State = HTTPRequestStates.Queued;
RequestQueue.Add(request);
}
}
private static string GetKeyForRequest(HTTPRequest request)
{
if (request.CurrentUri.IsFile)
return request.CurrentUri.ToString();
// proxyUri + requestUri
// HTTP and HTTPS needs different connections.
return
#if !BESTHTTP_DISABLE_PROXY
(request.Proxy != null ? new UriBuilder(request.Proxy.Address.Scheme, request.Proxy.Address.Host, request.Proxy.Address.Port).Uri.ToString() : string.Empty) +
#endif
new UriBuilder(request.CurrentUri.Scheme, request.CurrentUri.Host, request.CurrentUri.Port).Uri.ToString();
}
/// <summary>
/// Factory method to create a concrete connection object.
/// </summary>
private static ConnectionBase CreateConnection(HTTPRequest request, string serverUrl)
{
if (request.CurrentUri.IsFile)
return new FileConnection(serverUrl);
#if UNITY_WEBGL && !UNITY_EDITOR
return new WebGLConnection(serverUrl);
#else
return new HTTPConnection(serverUrl);
#endif
}
private static ConnectionBase FindOrCreateFreeConnection(HTTPRequest request)
{
ConnectionBase conn = null;
List<ConnectionBase> connections;
string serverUrl = GetKeyForRequest(request);
if (Connections.TryGetValue(serverUrl, out connections))
{
// count active connections
int activeConnections = 0;
for (int i = 0; i < connections.Count; ++i)
if (connections[i].IsActive)
activeConnections++;
if (activeConnections <= MaxConnectionPerServer)
// search for a Free connection
for (int i = 0; i < connections.Count && conn == null; ++i)
{
var tmpConn = connections[i];
if (tmpConn != null &&
tmpConn.IsFree &&
(
#if !BESTHTTP_DISABLE_PROXY
!tmpConn.HasProxy ||
#endif
tmpConn.LastProcessedUri == null ||
tmpConn.LastProcessedUri.Host.Equals(request.CurrentUri.Host, StringComparison.OrdinalIgnoreCase)))
conn = tmpConn;
}
}
else
Connections.Add(serverUrl, connections = new List<ConnectionBase>(MaxConnectionPerServer));
// No free connection found?
if (conn == null)
{
// Max connection reached?
if (connections.Count >= MaxConnectionPerServer)
return null;
// if no, create a new one
connections.Add(conn = CreateConnection(request, serverUrl));
}
return conn;
}
/// <summary>
/// Will return with true when there at least one request that can be processed from the RequestQueue.
/// </summary>
private static bool CanProcessFromQueue()
{
for (int i = 0; i < RequestQueue.Count; ++i)
if (FindOrCreateFreeConnection(RequestQueue[i]) != null)
return true;
return false;
}
private static void RecycleConnection(ConnectionBase conn)
{
conn.Recycle(OnConnectionRecylced);
}
private static void OnConnectionRecylced(ConnectionBase conn)
{
lock (RecycledConnections)
{
RecycledConnections.Add(conn);
}
}
#endregion
#region Internal Helper Functions
/// <summary>
/// Will return the ConnectionBase object that processing the given request.
/// </summary>
internal static ConnectionBase GetConnectionWith(HTTPRequest request)
{
lock (Locker)
{
for (int i = 0; i < ActiveConnections.Count; ++i)
{
var connection = ActiveConnections[i];
if (connection.CurrentRequest == request)
return connection;
}
return null;
}
}
internal static bool RemoveFromQueue(HTTPRequest request)
{
return RequestQueue.Remove(request);
}
#if !((BESTHTTP_DISABLE_CACHING && BESTHTTP_DISABLE_COOKIES) || (UNITY_WEBGL && !UNITY_EDITOR))
/// <summary>
/// Will return where the various caches should be saved.
/// </summary>
internal static string GetRootCacheFolder()
{
try
{
if (RootCacheFolderProvider != null)
return RootCacheFolderProvider();
}
catch(Exception ex)
{
HTTPManager.Logger.Exception("HTTPManager", "GetRootCacheFolder", ex);
}
#if NETFX_CORE
return Windows.Storage.ApplicationData.Current.LocalFolder.Path;
#else
return Application.persistentDataPath;
#endif
}
#endif
#endregion
#region MonoBehaviour Events (Called from HTTPUpdateDelegator)
/// <summary>
/// Update function that should be called regularly from a Unity event(Update, LateUpdate). Callbacks are dispatched from this function.
/// </summary>
public static void OnUpdate()
{
// We will try to acquire a lock. If it fails, we will skip this frame without calling any callback.
if (System.Threading.Monitor.TryEnter(Locker))
{
try
{
IsCallingCallbacks = true;
try
{
for (int i = 0; i < ActiveConnections.Count; ++i)
{
ConnectionBase conn = ActiveConnections[i];
switch (conn.State)
{
case HTTPConnectionStates.Processing:
conn.HandleProgressCallback();
if (conn.CurrentRequest.UseStreaming && conn.CurrentRequest.Response != null && conn.CurrentRequest.Response.HasStreamedFragments())
conn.HandleCallback();
try
{
if (((!conn.CurrentRequest.UseStreaming && conn.CurrentRequest.UploadStream == null) || conn.CurrentRequest.EnableTimoutForStreaming) &&
DateTime.UtcNow - conn.StartTime > conn.CurrentRequest.Timeout)
conn.Abort(HTTPConnectionStates.TimedOut);
}
catch (OverflowException)
{
HTTPManager.Logger.Warning("HTTPManager", "TimeSpan overflow");
}
break;
case HTTPConnectionStates.TimedOut:
// The connection is still in TimedOut state, and if we waited enough time, we will dispatch the
// callback and recycle the connection
try
{
if (DateTime.UtcNow - conn.TimedOutStart > TimeSpan.FromMilliseconds(500))
{
HTTPManager.Logger.Information("HTTPManager", "Hard aborting connection because of a long waiting TimedOut state");
conn.CurrentRequest.Response = null;
conn.CurrentRequest.State = HTTPRequestStates.TimedOut;
conn.HandleCallback();
// this will set the connection's CurrentRequest to null
RecycleConnection(conn);
}
}
catch(OverflowException)
{
HTTPManager.Logger.Warning("HTTPManager", "TimeSpan overflow");
}
break;
case HTTPConnectionStates.Redirected:
// If the server redirected us, we need to find or create a connection to the new server and send out the request again.
SendRequest(conn.CurrentRequest);
RecycleConnection(conn);
break;
case HTTPConnectionStates.WaitForRecycle:
// If it's a streamed request, it's finished now
conn.CurrentRequest.FinishStreaming();
// Call the callback
conn.HandleCallback();
// Then recycle the connection
RecycleConnection(conn);
break;
case HTTPConnectionStates.Upgraded:
// The connection upgraded to an other protocol
conn.HandleCallback();
break;
case HTTPConnectionStates.WaitForProtocolShutdown:
var ws = conn.CurrentRequest.Response as IProtocol;
if (ws != null)
ws.HandleEvents();
if (ws == null || ws.IsClosed)
{
conn.HandleCallback();
// After both sending and receiving a Close message, an endpoint considers the WebSocket connection closed and MUST close the underlying TCP connection.
conn.Dispose();
RecycleConnection(conn);
}
break;
case HTTPConnectionStates.AbortRequested:
// Corner case: we aborted a WebSocket connection
{
ws = conn.CurrentRequest.Response as IProtocol;
if (ws != null)
{
ws.HandleEvents();
if (ws.IsClosed)
{
conn.HandleCallback();
conn.Dispose();
RecycleConnection(conn);
}
}
}
break;
case HTTPConnectionStates.Closed:
// If it's a streamed request, it's finished now
conn.CurrentRequest.FinishStreaming();
// Call the callback
conn.HandleCallback();
// It will remove from the ActiveConnections
RecycleConnection(conn);
break;
case HTTPConnectionStates.Free:
RecycleConnection(conn);
break;
}
}
}
finally
{
IsCallingCallbacks = false;
}
// Just try to grab the lock, if we can't have it we can wait another turn because it isn't
// critical to do it right now.
if (System.Threading.Monitor.TryEnter(RecycledConnections))
try
{
if (RecycledConnections.Count > 0)
{
for (int i = 0; i < RecycledConnections.Count; ++i)
{
var connection = RecycledConnections[i];
// If in a callback made a request that acquired this connection, then we will not remove it from the
// active connections.
if (connection.IsFree)
{
ActiveConnections.Remove(connection);
FreeConnections.Add(connection);
}
}
RecycledConnections.Clear();
}
}
finally
{
System.Threading.Monitor.Exit(RecycledConnections);
}
if (FreeConnections.Count > 0)
for (int i = 0; i < FreeConnections.Count; i++)
{
var connection = FreeConnections[i];
if (connection.IsRemovable)
{
// Remove the connection from the connection reference table
List<ConnectionBase> connections = null;
if (Connections.TryGetValue(connection.ServerAddress, out connections))
connections.Remove(connection);
// Dispose the connection
connection.Dispose();
FreeConnections.RemoveAt(i);
i--;
}
}
if (CanProcessFromQueue())
{
// Sort the queue by priority, only if we have to
if (RequestQueue.Find((req) => req.Priority != 0) != null)
RequestQueue.Sort((req1, req2) => req1.Priority - req2.Priority);
// Create an array from the queue and clear it. When we call the SendRequest while still no room for new connections, the same queue will be rebuilt.
var queue = RequestQueue.ToArray();
RequestQueue.Clear();
for (int i = 0; i < queue.Length; ++i)
SendRequest(queue[i]);
}
}
finally
{
System.Threading.Monitor.Exit(Locker);
}
}
if (heartbeats != null)
heartbeats.Update();
}
public static void OnQuit()
{
lock (Locker)
{
#if !BESTHTTP_DISABLE_CACHING && (!UNITY_WEBGL || UNITY_EDITOR)
Caching.HTTPCacheService.SaveLibrary();
#endif
var queue = RequestQueue.ToArray();
RequestQueue.Clear();
foreach (var req in queue)
{
// Swallow any exceptions, we are quitting anyway.
try
{
req.Abort();
}
catch { }
}
// Close all TCP connections when the application is terminating.
foreach (var kvp in Connections)
{
foreach (var conn in kvp.Value)
{
// Swallow any exceptions, we are quitting anyway.
try
{
if (conn.CurrentRequest != null)
conn.CurrentRequest.State = HTTPRequestStates.Aborted;
conn.Abort(HTTPConnectionStates.Closed);
conn.Dispose();
}
catch { }
}
kvp.Value.Clear();
}
Connections.Clear();
OnUpdate();
}
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 991af424dbdf54dc28bb5380ec81a803
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,68 @@
namespace BestHTTP
{
/// <summary>
/// Some supported methods described in the rfc: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9
/// </summary>
public enum HTTPMethods : byte
{
/// <summary>
/// The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI.
/// If the Request-URI refers to a data-producing process, it is the produced data which shall be returned as the
/// entity in the response and not the source text of the process, unless that text happens to be the output of the process.
/// </summary>
Get,
/// <summary>
/// The HEAD method is identical to GET except that the server MUST NOT return a message-body in the response.
/// The metainformation contained in the HTTP headers in response to a HEAD request SHOULD be identical to the information sent in response to a GET request.
/// This method can be used for obtaining metainformation about the entity implied by the request without transferring the entity-body itself.
/// This method is often used for testing hypertext links for validity, accessibility, and recent modification.
/// </summary>
Head,
/// <summary>
/// The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line.
/// POST is designed to allow a uniform method to cover the following functions:
/// <list type="bullet">
/// <item><description>Annotation of existing resources;</description></item>
/// <item><description>Posting a message to a bulletin board, newsgroup, mailing list, or similar group of articles;</description></item>
/// <item><description>Providing a block of data, such as the result of submitting a form, to a data-handling process;</description></item>
/// <item><description>Extending a database through an append operation.</description></item>
/// </list>
/// The actual function performed by the POST method is determined by the server and is usually dependent on the Request-URI.
/// The posted entity is subordinate to that URI in the same way that a file is subordinate to a directory containing it,
/// a news article is subordinate to a newsgroup to which it is posted, or a record is subordinate to a database.
/// The action performed by the POST method might not result in a resource that can be identified by a URI. In this case,
/// either 200 (OK) or 204 (No Content) is the appropriate response status, depending on whether or not the response includes an entity that describes the result.
/// </summary>
Post,
/// <summary>
/// The PUT method requests that the enclosed entity be stored under the supplied Request-URI.
/// If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server.
/// If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent,
/// the origin server can create the resource with that URI. If a new resource is created, the origin server MUST inform the user agent via the 201 (Created) response.
/// If an existing resource is modified, either the 200 (OK) or 204 (No Content) response codes SHOULD be sent to indicate successful completion of the request.
/// If the resource could not be created or modified with the Request-URI, an appropriate error response SHOULD be given that reflects the nature of the problem.
/// The recipient of the entity MUST NOT ignore any Content-* (e.g. Content-Range) headers that it does not understand or implement and MUST return a 501 (Not Implemented) response in such cases.
/// </summary>
Put,
/// <summary>
/// The DELETE method requests that the origin server delete the resource identified by the Request-URI. This method MAY be overridden by human intervention (or other means) on the origin server.
/// The client cannot be guaranteed that the operation has been carried out, even if the status code returned from the origin server indicates that the action has been completed successfully.
/// However, the server SHOULD NOT indicate success unless, at the time the response is given, it intends to delete the resource or move it to an inaccessible location.
/// A successful response SHOULD be 200 (OK) if the response includes an entity describing the status, 202 (Accepted) if the action has not yet been enacted, or 204 (No Content)
/// if the action has been enacted but the response does not include an entity.
/// </summary>
Delete,
/// <summary>
/// http://tools.ietf.org/html/rfc5789
/// The PATCH method requests that a set of changes described in the request entity be applied to the resource identified by the Request-URI.
/// The set of changes is represented in a format called a "patchdocument" identified by a media type. If the Request-URI does not point to an existing resource,
/// the server MAY create a new resource, depending on the patch document type (whether it can logically modify a null resource) and permissions, etc.
/// </summary>
Patch
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 386343578ef3b4254b00479556ecd7d3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace BestHTTP
{
public enum SupportedProtocols
{
Unknown,
HTTP,
#if !BESTHTTP_DISABLE_WEBSOCKET
WebSocket,
#endif
#if !BESTHTTP_DISABLE_SERVERSENT_EVENTS
ServerSentEvents
#endif
}
internal static class HTTPProtocolFactory
{
public static HTTPResponse Get(SupportedProtocols protocol, HTTPRequest request, Stream stream, bool isStreamed, bool isFromCache)
{
switch (protocol)
{
#if !BESTHTTP_DISABLE_WEBSOCKET && (!UNITY_WEBGL || UNITY_EDITOR)
case SupportedProtocols.WebSocket: return new WebSocket.WebSocketResponse(request, stream, isStreamed, isFromCache);
#endif
#if !BESTHTTP_DISABLE_SERVERSENT_EVENTS && (!UNITY_WEBGL || UNITY_EDITOR)
case SupportedProtocols.ServerSentEvents: return new ServerSentEvents.EventSourceResponse(request, stream, isStreamed, isFromCache);
#endif
default: return new HTTPResponse(request, stream, isStreamed, isFromCache);
}
}
public static SupportedProtocols GetProtocolFromUri(Uri uri)
{
if (uri == null || uri.Scheme == null)
throw new Exception("Malformed URI in GetProtocolFromUri");
string scheme = uri.Scheme.ToLowerInvariant();
switch (scheme)
{
#if !BESTHTTP_DISABLE_WEBSOCKET
case "ws":
case "wss":
return SupportedProtocols.WebSocket;
#endif
default:
return SupportedProtocols.HTTP;
}
}
public static bool IsSecureProtocol(Uri uri)
{
if (uri == null || uri.Scheme == null)
throw new Exception("Malformed URI in IsSecureProtocol");
string scheme = uri.Scheme.ToLowerInvariant();
switch (scheme)
{
// http
case "https":
#if !BESTHTTP_DISABLE_WEBSOCKET
// WebSocket
case "wss":
#endif
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f0b8eb60f29da4448a9b93a3670245b6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,62 @@
#if !BESTHTTP_DISABLE_PROXY
using System;
using BestHTTP.Authentication;
namespace BestHTTP
{
public sealed class HTTPProxy
{
/// <summary>
/// Address of the proxy server. It has to be in the http://proxyaddress:port form.
/// </summary>
public Uri Address { get; set; }
/// <summary>
/// Credentials of the proxy
/// </summary>
public Credentials Credentials { get; set; }
/// <summary>
/// True if the proxy can act as a transparent proxy
/// </summary>
public bool IsTransparent { get; set; }
/// <summary>
/// Some non-transparent proxies are except only the path and query of the request uri. Default value is true
/// </summary>
public bool SendWholeUri { get; set; }
/// <summary>
/// Regardless of the value of IsTransparent, for secure protocols(HTTPS://, WSS://) the plugin will use the proxy as an explicit proxy(will issue a CONNECT request to the proxy)
/// </summary>
public bool NonTransparentForHTTPS { get; set; }
public HTTPProxy(Uri address)
:this(address, null, false)
{}
public HTTPProxy(Uri address, Credentials credentials)
:this(address, credentials, false)
{}
public HTTPProxy(Uri address, Credentials credentials, bool isTransparent)
:this(address, credentials, isTransparent, true)
{ }
public HTTPProxy(Uri address, Credentials credentials, bool isTransparent, bool sendWholeUri)
: this(address, credentials, isTransparent, true, true)
{ }
public HTTPProxy(Uri address, Credentials credentials, bool isTransparent, bool sendWholeUri, bool nonTransparentForHTTPS)
{
this.Address = address;
this.Credentials = credentials;
this.IsTransparent = isTransparent;
this.SendWholeUri = sendWholeUri;
this.NonTransparentForHTTPS = nonTransparentForHTTPS;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 88343256a8f9a42d780911436b0ab9c5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,57 @@
using System;
namespace BestHTTP
{
/// <summary>
///
/// </summary>
public sealed class HTTPRange
{
/// <summary>
/// The first byte's position that the server sent.
/// </summary>
public int FirstBytePos { get; private set; }
/// <summary>
/// The last byte's position that the server sent.
/// </summary>
public int LastBytePos { get; private set; }
/// <summary>
/// Indicates the total length of the full entity-body on the server, -1 if this length is unknown or difficult to determine.
/// </summary>
public int ContentLength { get; private set; }
/// <summary>
///
/// </summary>
public bool IsValid { get; private set; }
internal HTTPRange()
{
this.ContentLength = -1;
this.IsValid = false;
}
internal HTTPRange(int contentLength)
{
this.ContentLength = contentLength;
this.IsValid = false;
}
internal HTTPRange(int firstBytePosition, int lastBytePosition, int contentLength)
{
this.FirstBytePos = firstBytePosition;
this.LastBytePos = lastBytePosition;
this.ContentLength = contentLength;
// A byte-content-range-spec with a byte-range-resp-spec whose last-byte-pos value is less than its first-byte-pos value, or whose instance-length value is less than or equal to its last-byte-pos value, is invalid.
this.IsValid = this.FirstBytePos <= this.LastBytePos && this.ContentLength > this.LastBytePos;
}
public override string ToString()
{
return string.Format("{0}-{1}/{2} (valid: {3})", FirstBytePos, LastBytePos, ContentLength, IsValid);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 78725c800df5d4fd3ae6fe33353feb89
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f10d977f41b02446ea8cca1538509036
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c1687f6f677b94ef38626e07d4d7c4a1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,221 @@
using UnityEngine;
#if NETFX_CORE || BUILD_FOR_WP8
using System.Threading.Tasks;
#endif
namespace BestHTTP
{
/// <summary>
/// Will route some U3D calls to the HTTPManager.
/// </summary>
[ExecuteInEditMode]
public sealed class HTTPUpdateDelegator : MonoBehaviour
{
#region Public Properties
/// <summary>
/// The singleton instance of the HTTPUpdateDelegator
/// </summary>
public static HTTPUpdateDelegator Instance { get; private set; }
/// <summary>
/// True, if the Instance property should hold a valid value.
/// </summary>
public static bool IsCreated { get; private set; }
/// <summary>
/// Set it true before any CheckInstance() call, or before any request send to dispatch callbacks on another thread.
/// </summary>
public static bool IsThreaded { get; set; }
/// <summary>
/// It's true if the dispatch thread running.
/// </summary>
public static bool IsThreadRunning { get; private set; }
/// <summary>
/// How much time the plugin should wait between two update call. Its default value 100 ms.
/// </summary>
public static int ThreadFrequencyInMS { get; set; }
/// <summary>
/// Called in the OnApplicationQuit function. If this function returns False, the plugin will not start to
/// shut down itself.
/// </summary>
public static System.Func<bool> OnBeforeApplicationQuit;
#endregion
private static bool IsSetupCalled;
static HTTPUpdateDelegator()
{
ThreadFrequencyInMS = 100;
}
/// <summary>
/// Will create the HTTPUpdateDelegator instance and set it up.
/// </summary>
public static void CheckInstance()
{
try
{
if (!IsCreated)
{
GameObject go = GameObject.Find("HTTP Update Delegator");
if (go != null)
Instance = go.GetComponent<HTTPUpdateDelegator>();
if (Instance == null)
{
go = new GameObject("HTTP Update Delegator");
go.hideFlags = HideFlags.HideAndDontSave;
#if UNITY_EDITOR
if (UnityEditor.EditorApplication.isPlaying)
GameObject.DontDestroyOnLoad(go);
#endif
Instance = go.AddComponent<HTTPUpdateDelegator>();
}
IsCreated = true;
#if UNITY_EDITOR
if (!UnityEditor.EditorApplication.isPlaying)
{
UnityEditor.EditorApplication.update -= Instance.Update;
UnityEditor.EditorApplication.update += Instance.Update;
}
UnityEditor.EditorApplication.playmodeStateChanged -= Instance.OnPlayModeStateChanged;
UnityEditor.EditorApplication.playmodeStateChanged += Instance.OnPlayModeStateChanged;
#endif
}
}
catch
{
HTTPManager.Logger.Error("HTTPUpdateDelegator", "Please call the BestHTTP.HTTPManager.Setup() from one of Unity's event(eg. awake, start) before you send any request!");
}
}
private void Setup()
{
#if !BESTHTTP_DISABLE_CACHING && (!UNITY_WEBGL || UNITY_EDITOR)
Caching.HTTPCacheService.SetupCacheFolder();
#endif
#if !BESTHTTP_DISABLE_COOKIES && (!UNITY_WEBGL || UNITY_EDITOR)
Cookies.CookieJar.SetupFolder();
Cookies.CookieJar.Load();
#endif
#if UNITY_WEBGL
// Threads are not implemented in WEBGL builds, disable it for now.
IsThreaded = false;
#endif
if (IsThreaded)
{
#if NETFX_CORE
Windows.System.Threading.ThreadPool.RunAsync(ThreadFunc);
#else
new System.Threading.Thread(ThreadFunc)
.Start();
#endif
}
IsSetupCalled = true;
}
#if NETFX_CORE
async
#endif
void ThreadFunc(object obj)
{
HTTPManager.Logger.Information ("HTTPUpdateDelegator", "Update Thread Started");
try
{
IsThreadRunning = true;
while (IsThreadRunning)
{
HTTPManager.OnUpdate();
#if NETFX_CORE
await Task.Delay(ThreadFrequencyInMS);
#else
System.Threading.Thread.Sleep(ThreadFrequencyInMS);
#endif
}
}
finally
{
HTTPManager.Logger.Information("HTTPUpdateDelegator", "Update Thread Ended");
}
}
void Update()
{
if (!IsSetupCalled)
{
IsSetupCalled = true;
Setup();
}
if (!IsThreaded)
HTTPManager.OnUpdate();
}
#if UNITY_EDITOR
void OnPlayModeStateChanged()
{
if (UnityEditor.EditorApplication.isPlaying)
UnityEditor.EditorApplication.update -= Update;
else if (!UnityEditor.EditorApplication.isPlaying)
UnityEditor.EditorApplication.update += Update;
}
#endif
void OnDisable()
{
#if UNITY_EDITOR
if (UnityEditor.EditorApplication.isPlaying)
#endif
OnApplicationQuit();
}
void OnApplicationQuit()
{
if (OnBeforeApplicationQuit != null)
{
try
{
if (!OnBeforeApplicationQuit())
{
HTTPManager.Logger.Information("HTTPUpdateDelegator", "OnBeforeApplicationQuit call returned false, postponing plugin shutdown.");
return;
}
}
catch(System.Exception ex)
{
HTTPManager.Logger.Exception("HTTPUpdateDelegator", string.Empty, ex);
}
}
IsThreadRunning = false;
if (!IsCreated)
return;
IsCreated = false;
HTTPManager.OnQuit();
#if UNITY_EDITOR
UnityEditor.EditorApplication.update -= Update;
UnityEditor.EditorApplication.playmodeStateChanged -= OnPlayModeStateChanged;
#endif
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a0edd6970b0bc4beb93ca36ae6568af2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a8391a713cd0f4b5b901757691e6d280
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More