提交Unity 联机Pro

This commit is contained in:
PC-20230316NUNE\Administrator
2024-08-17 14:27:18 +08:00
parent f00193b000
commit 894100ae37
7448 changed files with 854473 additions and 0 deletions

View File

@@ -0,0 +1,487 @@
//
// System.IO.MemoryStream.cs
//
// Authors: Marcin Szczepanski (marcins@zipworld.com.au)
// Patrik Torstensson
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
//
// (c) 2001,2002 Marcin Szczepanski, Patrik Torstensson
// (c) 2003 Ximian, Inc. (http://www.ximian.com)
// Copyright (C) 2004 Novell (http://www.novell.com)
//
//
// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.IO;
using BestHTTP.PlatformSupport.Memory;
namespace BestHTTP.Extensions
{
/// <summary>
/// This is a modified MemoryStream class to use VariableSizedBufferPool
/// </summary>
public sealed class BufferPoolMemoryStream : System.IO.Stream
{
bool canWrite;
bool allowGetBuffer;
int capacity;
int length;
byte[] internalBuffer;
int initialIndex;
bool expandable;
bool streamClosed;
int position;
int dirty_bytes;
bool releaseInternalBuffer;
public BufferPoolMemoryStream() : this(0)
{
}
public BufferPoolMemoryStream(int capacity)
{
if (capacity < 0)
throw new ArgumentOutOfRangeException("capacity");
canWrite = true;
//internalBuffer = capacity > 0 ? BufferPool.Get(capacity, true) : BufferPool.NoData;
//this.capacity = internalBuffer.Length;
//
//expandable = true;
//allowGetBuffer = true;
var buffer = capacity > 0 ? BufferPool.Get(capacity, true) : BufferPool.NoData;
InternalConstructor(buffer, 0, buffer.Length, true, true, true, true);
}
public BufferPoolMemoryStream(byte[] buffer)
{
if (buffer == null)
throw new ArgumentNullException("buffer");
InternalConstructor(buffer, 0, buffer.Length, true, false, true, false);
}
public BufferPoolMemoryStream(byte[] buffer, bool writable)
{
if (buffer == null)
throw new ArgumentNullException("buffer");
InternalConstructor(buffer, 0, buffer.Length, writable, false, true, false);
}
public BufferPoolMemoryStream(byte[] buffer, int index, int count)
{
InternalConstructor(buffer, index, count, true, false, true, false);
}
public BufferPoolMemoryStream(byte[] buffer, int index, int count, bool writable)
{
InternalConstructor(buffer, index, count, writable, false, true, false);
}
public BufferPoolMemoryStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible)
{
InternalConstructor(buffer, index, count, writable, publiclyVisible, true, false);
}
public BufferPoolMemoryStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible, bool releaseBuffer)
{
InternalConstructor(buffer, index, count, writable, publiclyVisible, releaseBuffer, false);
}
public BufferPoolMemoryStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible, bool releaseBuffer, bool canExpand)
{
InternalConstructor(buffer, index, count, writable, publiclyVisible, releaseBuffer, canExpand);
}
void InternalConstructor(byte[] buffer, int index, int count, bool writable, bool publicallyVisible, bool releaseBuffer, bool canExpand)
{
if (buffer == null)
throw new ArgumentNullException("buffer");
if (index < 0 || count < 0)
throw new ArgumentOutOfRangeException("index or count is less than 0.");
if (buffer.Length - index < count)
throw new ArgumentException("index+count",
"The size of the buffer is less than index + count.");
canWrite = writable;
internalBuffer = buffer;
capacity = count + index;
//length = capacity;
length = 0;
position = index;
initialIndex = index;
allowGetBuffer = publicallyVisible;
releaseInternalBuffer = releaseBuffer;
expandable = canExpand;
}
void CheckIfClosedThrowDisposed()
{
if (streamClosed)
throw new ObjectDisposedException("MemoryStream");
}
public override bool CanRead
{
get { return !streamClosed; }
}
public override bool CanSeek
{
get { return !streamClosed; }
}
public override bool CanWrite
{
get { return (!streamClosed && canWrite); }
}
public int Capacity
{
get
{
CheckIfClosedThrowDisposed();
return capacity - initialIndex;
}
set
{
CheckIfClosedThrowDisposed();
if (value == capacity)
return; // LAMENESS: see MemoryStreamTest.ConstructorFive
if (!expandable)
throw new NotSupportedException("Cannot expand this MemoryStream");
if (value < 0 || value < length)
throw new ArgumentOutOfRangeException("value",
"New capacity cannot be negative or less than the current capacity " + value + " " + capacity);
byte[] newBuffer = null;
if (value != 0)
{
newBuffer = BufferPool.Get(value, true);
Buffer.BlockCopy(internalBuffer, 0, newBuffer, 0, length);
}
dirty_bytes = 0; // discard any dirty area beyond previous length
BufferPool.Release(internalBuffer);
internalBuffer = newBuffer; // It's null when capacity is set to 0
capacity = internalBuffer != null ? internalBuffer.Length : 0;
}
}
public override long Length
{
get
{
// LAMESPEC: The spec says to throw an IOException if the
// stream is closed and an ObjectDisposedException if
// "methods were called after the stream was closed". What
// is the difference?
CheckIfClosedThrowDisposed();
// This is ok for MemoryStreamTest.ConstructorFive
return length - initialIndex;
}
}
public override long Position
{
get
{
CheckIfClosedThrowDisposed();
return position - initialIndex;
}
set
{
CheckIfClosedThrowDisposed();
if (value < 0)
throw new ArgumentOutOfRangeException("value",
"Position cannot be negative");
if (value > Int32.MaxValue)
throw new ArgumentOutOfRangeException("value",
"Position must be non-negative and less than 2^31 - 1 - origin");
position = initialIndex + (int)value;
}
}
protected override void Dispose (bool disposing)
{
streamClosed = true;
expandable = false;
if (disposing && internalBuffer != null && this.releaseInternalBuffer)
BufferPool.Release(internalBuffer);
internalBuffer = null;
}
public override void Flush()
{
// Do nothing
}
public byte[] GetBuffer()
{
if (!allowGetBuffer)
throw new UnauthorizedAccessException();
return internalBuffer;
}
public override int Read(byte[] buffer, int offset, int count)
{
CheckIfClosedThrowDisposed();
if (buffer == null)
throw new ArgumentNullException("buffer");
if (offset < 0 || count < 0)
throw new ArgumentOutOfRangeException("offset or count less than zero.");
if (buffer.Length - offset < count)
throw new ArgumentException("offset+count",
"The size of the buffer is less than offset + count.");
if (position >= length || count == 0)
return 0;
if (position > length - count)
count = length - position;
Buffer.BlockCopy(internalBuffer, position, buffer, offset, count);
position += count;
return count;
}
public override int ReadByte()
{
CheckIfClosedThrowDisposed();
if (position >= length)
return -1;
return internalBuffer[position++];
}
public override long Seek(long offset, SeekOrigin loc)
{
CheckIfClosedThrowDisposed();
// It's funny that they don't throw this exception for < Int32.MinValue
if (offset > (long)Int32.MaxValue)
throw new ArgumentOutOfRangeException("Offset out of range. " + offset);
int refPoint;
switch (loc)
{
case SeekOrigin.Begin:
if (offset < 0)
throw new IOException("Attempted to seek before start of MemoryStream.");
refPoint = initialIndex;
break;
case SeekOrigin.Current:
refPoint = position;
break;
case SeekOrigin.End:
refPoint = length;
break;
default:
throw new ArgumentException("loc", "Invalid SeekOrigin");
}
// LAMESPEC: My goodness, how may LAMESPECs are there in this
// class! :) In the spec for the Position property it's stated
// "The position must not be more than one byte beyond the end of the stream."
// In the spec for seek it says "Seeking to any location beyond the length of the
// stream is supported." That's a contradiction i'd say.
// I guess seek can go anywhere but if you use position it may get moved back.
refPoint += (int)offset;
if (refPoint < initialIndex)
throw new IOException("Attempted to seek before start of MemoryStream.");
position = refPoint;
return position;
}
int CalculateNewCapacity(int minimum)
{
if (minimum < 256)
minimum = 256; // See GetBufferTwo test
if (minimum < capacity * 2)
minimum = capacity * 2;
if (!UnityEngine.Mathf.IsPowerOfTwo(minimum))
minimum = UnityEngine.Mathf.NextPowerOfTwo(minimum);
return minimum;
}
void Expand(int newSize)
{
// We don't need to take into account the dirty bytes when incrementing the
// Capacity, as changing it will only preserve the valid clear region.
if (newSize > capacity)
Capacity = CalculateNewCapacity(newSize);
else if (dirty_bytes > 0)
{
Array.Clear(internalBuffer, length, dirty_bytes);
dirty_bytes = 0;
}
}
public override void SetLength(long value)
{
if (!expandable && value > capacity)
throw new NotSupportedException("Expanding this MemoryStream is not supported");
CheckIfClosedThrowDisposed();
if (!canWrite)
{
throw new NotSupportedException("Cannot write to this MemoryStream");
}
// LAMESPEC: AGAIN! It says to throw this exception if value is
// greater than "the maximum length of the MemoryStream". I haven't
// seen anywhere mention what the maximum length of a MemoryStream is and
// since we're this far this memory stream is expandable.
if (value < 0 || (value + initialIndex) > (long)Int32.MaxValue)
throw new ArgumentOutOfRangeException();
int newSize = (int)value + initialIndex;
if (newSize > length)
Expand(newSize);
else if (newSize < length) // Postpone the call to Array.Clear till expand time
dirty_bytes += length - newSize;
length = newSize;
if (position > length)
position = length;
}
public byte[] ToArray()
{
return ToArray(false);
}
public byte[] ToArray(bool canBeLarger)
{
int l = length - initialIndex;
byte[] outBuffer = null;
if (l > 0)
{
if (canBeLarger)
outBuffer = BufferPool.Get(l, true);
else
outBuffer = new byte[l];
}
else
{
outBuffer = BufferPool.NoData;
}
if (internalBuffer != null)
Buffer.BlockCopy(internalBuffer, initialIndex, outBuffer, 0, l);
return outBuffer;
}
public BufferSegment ToBufferSegment()
{
int l = length - initialIndex;
byte[] outBuffer = l > 0 ? BufferPool.Get(l, true) : BufferPool.NoData;
if (internalBuffer != null)
Buffer.BlockCopy(internalBuffer, initialIndex, outBuffer, 0, l);
return new BufferSegment(outBuffer, 0, l);
}
public override void Write(byte[] buffer, int offset, int count)
{
CheckIfClosedThrowDisposed();
if (!canWrite)
throw new NotSupportedException("Cannot write to this stream.");
if (buffer == null)
throw new ArgumentNullException("buffer");
if (offset < 0 || count < 0)
throw new ArgumentOutOfRangeException();
if (buffer.Length - offset < count)
throw new ArgumentException("offset+count",
"The size of the buffer is less than offset + count.");
// reordered to avoid possible integer overflow
if (position > length - count)
Expand(position + count);
Buffer.BlockCopy(buffer, offset, internalBuffer, position, count);
position += count;
if (position >= length)
length = position;
}
public override void WriteByte(byte value)
{
CheckIfClosedThrowDisposed();
if (!canWrite)
throw new NotSupportedException("Cannot write to this stream.");
if (position >= length)
{
Expand(position + 1);
length = position + 1;
}
internalBuffer[position++] = value;
}
public void WriteTo(Stream stream)
{
CheckIfClosedThrowDisposed();
if (stream == null)
throw new ArgumentNullException("stream");
stream.Write(internalBuffer, initialIndex, length - initialIndex);
}
}
}

View File

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

View File

@@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.IO;
using BestHTTP.PlatformSupport.Memory;
namespace BestHTTP.Extensions
{
public class BufferSegmentStream : Stream
{
public override bool CanRead { get { return false; } }
public override bool CanSeek { get { return false; } }
public override bool CanWrite { get { return false; } }
public override long Length { get { return this._length; } }
protected long _length;
public override long Position { get { return 0; } set { } }
protected List<BufferSegment> bufferList = new List<BufferSegment>();
private byte[] _tempByteArray = new byte[1];
public override int ReadByte()
{
if (Read(this._tempByteArray, 0, 1) == 0)
return -1;
return this._tempByteArray[0];
}
public override int Read(byte[] buffer, int offset, int count)
{
int sumReadCount = 0;
while (count > 0 && bufferList.Count > 0)
{
BufferSegment buff = this.bufferList[0];
int readCount = Math.Min(count, buff.Count);
Array.Copy(buff.Data, buff.Offset, buffer, offset, readCount);
sumReadCount += readCount;
offset += readCount;
count -= readCount;
this.bufferList[0] = buff = buff.Slice(buff.Offset + readCount);
if (buff.Count == 0)
{
this.bufferList.RemoveAt(0);
BufferPool.Release(buff.Data);
}
}
this._length -= sumReadCount;
return sumReadCount;
}
public override void Write(byte[] buffer, int offset, int count)
{
Write(new BufferSegment(buffer, offset, count));
}
public virtual void Write(BufferSegment bufferSegment)
{
this.bufferList.Add(bufferSegment);
this._length += bufferSegment.Count;
}
public virtual void Reset()
{
for (int i = 0; i < this.bufferList.Count; ++i)
BufferPool.Release(this.bufferList[i]);
this.bufferList.Clear();
this._length = 0;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
Reset();
}
public override void Flush() { }
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 163c4b09e7c509c4aaffd21e9961824c
timeCreated: 1574255448
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,76 @@
using System;
namespace BestHTTP.Extensions
{
public sealed class CircularBuffer<T>
{
public int Capacity { get; private set; }
public int Count { get; private set; }
public int StartIdx { get { return this.startIdx; } }
public int EndIdx { get { return this.endIdx; } }
public T this[int idx]
{
get
{
int realIdx = (this.startIdx + idx) % this.Capacity;
return this.buffer[realIdx];
}
set
{
int realIdx = (this.startIdx + idx) % this.Capacity;
this.buffer[realIdx] = value;
}
}
private T[] buffer;
private int startIdx;
private int endIdx;
public CircularBuffer(int capacity)
{
this.Capacity = capacity;
}
public void Add(T element)
{
if (this.buffer == null)
this.buffer = new T[this.Capacity];
this.buffer[this.endIdx] = element;
this.endIdx = (this.endIdx + 1) % this.Capacity;
if (this.endIdx == this.startIdx)
this.startIdx = (this.startIdx + 1) % this.Capacity;
this.Count = Math.Min(this.Count + 1, this.Capacity);
}
public void Clear()
{
this.Count = this.startIdx = this.endIdx = 0;
}
public override string ToString()
{
var sb = PlatformSupport.Text.StringBuilderPool.Get(2);
sb.Append("[");
int idx = this.startIdx;
while (idx != this.endIdx)
{
sb.Append(this.buffer[idx].ToString());
idx = (idx + 1) % this.Capacity;
if (idx != this.endIdx)
sb.Append("; ");
}
sb.Append("]");
return sb.ToString();
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: d3e42ed97af74b54fba27235416b1191
timeCreated: 1508423928
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,519 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using BestHTTP.PlatformSupport.Text;
#if NETFX_CORE
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
using Windows.Storage.Streams;
#else
using Cryptography = System.Security.Cryptography;
using FileStream = System.IO.FileStream;
#endif
using BestHTTP.PlatformSupport.Memory;
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 = StringBuilderPool.Get(bytes.Length); //new StringBuilder(bytes.Length);
foreach (byte b in bytes)
sb.Append(b <= 0x7f ? (char)b : '?');
return StringBuilderPool.ReleaseAndGrab(sb);
}
/// <summary>
/// On WP8 platform there are no ASCII encoding.
/// </summary>
public static BufferSegment GetASCIIBytes(this string str)
{
byte[] result = BufferPool.Get(str.Length, true);
for (int i = 0; i < str.Length; ++i)
{
char ch = str[i];
result[i] = (byte)((ch < (char)0x80) ? ch : '?');
}
return new BufferSegment(result, 0, str.Length);
}
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 Stream fs)
{
fs.Write(HTTPRequest.EOL, 0, 2);
}
public static void WriteLine(this Stream fs, string line)
{
var buff = line.GetASCIIBytes();
fs.Write(buff.Data, buff.Offset, buff.Count);
fs.WriteLine();
BufferPool.Release(buff);
}
public static void WriteLine(this Stream fs, string format, params object[] values)
{
var buff = string.Format(format, values).GetASCIIBytes();
fs.Write(buff.Data, buff.Offset, buff.Count);
fs.WriteLine();
BufferPool.Release(buff);
}
#endregion
#region Other Extensions
public static BufferSegment AsBuffer(this byte[] bytes)
{
return new BufferSegment(bytes, 0, bytes.Length);
}
public static BufferSegment AsBuffer(this byte[] bytes, int length)
{
return new BufferSegment(bytes, 0, length);
}
public static BufferSegment AsBuffer(this byte[] bytes, int offset, int length)
{
return new BufferSegment(bytes, offset, length);
}
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.ToLowerInvariant().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
option = option.ToLowerInvariant();
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 string[] FindOption(this string[] options, string option)
{
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);
}
public static void WriteBufferSegment(this Stream stream, BufferSegment buffer)
{
stream.Write(buffer.Data, buffer.Offset, buffer.Count);
}
/// <summary>
/// Returns true if the Uri's host is a valid IPv4 or IPv6 address.
/// </summary>
public static bool IsHostIsAnIPAddress(this Uri uri)
{
if (uri == null)
return false;
return IsIpV4AddressValid(uri.Host) || IsIpV6AddressValid(uri.Host);
}
// Original idea from: https://www.code4copy.com/csharp/c-validate-ip-address-string/
// Working regex: https://www.regular-expressions.info/ip.html
private static readonly System.Text.RegularExpressions.Regex validIpV4AddressRegex = new System.Text.RegularExpressions.Regex("\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
/// <summary>
/// Validates an IPv4 address.
/// </summary>
public static bool IsIpV4AddressValid(string address)
{
if (!string.IsNullOrEmpty(address))
return validIpV4AddressRegex.IsMatch(address.Trim());
return false;
}
/// <summary>
/// Validates an IPv6 address.
/// </summary>
public static bool IsIpV6AddressValid(string address)
{
#if !NETFX_CORE
if (!string.IsNullOrEmpty(address))
{
System.Net.IPAddress ip;
if (System.Net.IPAddress.TryParse(address, out ip))
return ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6;
}
#endif
return false;
}
#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, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out defaultValue);
return defaultValue.ToUniversalTime();
}
catch
{
return defaultValue;
}
}
public static string ToStrOrEmpty(this string str)
{
if (str == null)
return String.Empty;
return str;
}
public static string ToStr(this string str, string defaultVale)
{
if (str == null)
return defaultVale;
return str;
}
public static string ToBinaryStr(this byte value)
{
return Convert.ToString(value, 2).PadLeft(8, '0');
}
#endregion
#region MD5 Hashing
public static string CalculateMD5Hash(this string input)
{
var asciiBuff = input.GetASCIIBytes();
var hash = asciiBuff.CalculateMD5Hash();
BufferPool.Release(asciiBuff);
return hash;
}
public static string CalculateMD5Hash(this BufferSegment input)
{
#if NETFX_CORE
var alg = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Md5);
//IBuffer buff = CryptographicBuffer.CreateFromByteArray(input);
IBuffer buff = System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBufferExtensions.AsBuffer(input.Data, input.Offset, input.Count);
var hashed = alg.HashData(buff);
var res = CryptographicBuffer.EncodeToHexString(hashed);
return res;
#else
using (var md5 = Cryptography.MD5.Create()) {
var hash = md5.ComputeHash(input.Data, input.Offset, input.Count);
var sb = StringBuilderPool.Get(hash.Length); //new StringBuilder(hash.Length);
for (int i = 0; i < hash.Length; ++i)
sb.Append(hash[i].ToString("x2"));
BufferPool.Release(hash);
return StringBuilderPool.ReleaseAndGrab(sb);
}
#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, (ch) => ch != ',' && ch != ';', 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);
}
public static void ReadBuffer(this Stream stream, byte[] buffer, int length)
{
int count = 0;
do
{
int read = stream.Read(buffer, count, length - count);
if (read <= 0)
throw ExceptionHelper.ServerClosedTCPStream();
count += read;
} while (count < length);
}
#endregion
#region BufferPoolMemoryStream
public static void WriteString(this BufferPoolMemoryStream ms, string str)
{
var byteCount = Encoding.UTF8.GetByteCount(str);
byte[] buffer = BufferPool.Get(byteCount, true);
Encoding.UTF8.GetBytes(str, 0, str.Length, buffer, 0);
ms.Write(buffer, 0, byteCount);
BufferPool.Release(buffer);
}
public static void WriteLine(this BufferPoolMemoryStream ms)
{
ms.Write(HTTPRequest.EOL, 0, HTTPRequest.EOL.Length);
}
public static void WriteLine(this BufferPoolMemoryStream ms, string str)
{
ms.WriteString(str);
ms.Write(HTTPRequest.EOL, 0, HTTPRequest.EOL.Length);
}
#endregion
#if NET_STANDARD_2_0 || NETFX_CORE || NET_4_6
public static void Clear<T>(this System.Collections.Concurrent.ConcurrentQueue<T> queue)
{
T result;
while (queue.TryDequeue(out result))
;
}
#endif
}
public static class ExceptionHelper
{
public static Exception ServerClosedTCPStream()
{
return new Exception("TCP Stream closed unexpectedly by the remote server");
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 3ed17defed57cb64697daf74b5bb5873
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,409 @@
// Based on https://github.com/nickgravelyn/UnityToolbag/blob/master/Future/Future.cs
/*
* The MIT License (MIT)
Copyright (c) 2017, Nick Gravelyn
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
* */
using System;
using System.Collections.Generic;
namespace BestHTTP.Futures
{
/// <summary>
/// Describes the state of a future.
/// </summary>
public enum FutureState
{
/// <summary>
/// The future hasn't begun to resolve a value.
/// </summary>
Pending,
/// <summary>
/// The future is working on resolving a value.
/// </summary>
Processing,
/// <summary>
/// The future has a value ready.
/// </summary>
Success,
/// <summary>
/// The future failed to resolve a value.
/// </summary>
Error
}
/// <summary>
/// Defines the interface of an object that can be used to track a future value.
/// </summary>
/// <typeparam name="T">The type of object being retrieved.</typeparam>
public interface IFuture<T>
{
/// <summary>
/// Gets the state of the future.
/// </summary>
FutureState state { get; }
/// <summary>
/// Gets the value if the State is Success.
/// </summary>
T value { get; }
/// <summary>
/// Gets the failure exception if the State is Error.
/// </summary>
Exception error { get; }
/// <summary>
/// Adds a new callback to invoke when an intermediate result is known.
/// </summary>
/// <param name="callback">The callback to invoke.</param>
/// <returns>The future so additional calls can be chained together.</returns>
IFuture<T> OnItem(FutureValueCallback<T> callback);
/// <summary>
/// Adds a new callback to invoke if the future value is retrieved successfully.
/// </summary>
/// <param name="callback">The callback to invoke.</param>
/// <returns>The future so additional calls can be chained together.</returns>
IFuture<T> OnSuccess(FutureValueCallback<T> callback);
/// <summary>
/// Adds a new callback to invoke if the future has an error.
/// </summary>
/// <param name="callback">The callback to invoke.</param>
/// <returns>The future so additional calls can be chained together.</returns>
IFuture<T> OnError(FutureErrorCallback callback);
/// <summary>
/// Adds a new callback to invoke if the future value is retrieved successfully or has an error.
/// </summary>
/// <param name="callback">The callback to invoke.</param>
/// <returns>The future so additional calls can be chained together.</returns>
IFuture<T> OnComplete(FutureCallback<T> callback);
}
/// <summary>
/// Defines the signature for callbacks used by the future.
/// </summary>
/// <param name="future">The future.</param>
public delegate void FutureCallback<T>(IFuture<T> future);
public delegate void FutureValueCallback<T>(T value);
public delegate void FutureErrorCallback(Exception error);
/// <summary>
/// An implementation of <see cref="IFuture{T}"/> that can be used internally by methods that return futures.
/// </summary>
/// <remarks>
/// Methods should always return the <see cref="IFuture{T}"/> interface when calling code requests a future.
/// This class is intended to be constructed internally in the method to provide a simple implementation of
/// the interface. By returning the interface instead of the class it ensures the implementation can change
/// later on if requirements change, without affecting the calling code.
/// </remarks>
/// <typeparam name="T">The type of object being retrieved.</typeparam>
public class Future<T> : IFuture<T>
{
private volatile FutureState _state;
private T _value;
private Exception _error;
private Func<T> _processFunc;
private readonly List<FutureValueCallback<T>> _itemCallbacks = new List<FutureValueCallback<T>>();
private readonly List<FutureValueCallback<T>> _successCallbacks = new List<FutureValueCallback<T>>();
private readonly List<FutureErrorCallback> _errorCallbacks = new List<FutureErrorCallback>();
private readonly List<FutureCallback<T>> _complationCallbacks = new List<FutureCallback<T>>();
/// <summary>
/// Gets the state of the future.
/// </summary>
public FutureState state { get { return _state; } }
/// <summary>
/// Gets the value if the State is Success.
/// </summary>
public T value
{
get
{
if (_state != FutureState.Success && _state != FutureState.Processing)
{
throw new InvalidOperationException("value is not available unless state is Success or Processing.");
}
return _value;
}
}
/// <summary>
/// Gets the failure exception if the State is Error.
/// </summary>
public Exception error
{
get
{
if (_state != FutureState.Error)
{
throw new InvalidOperationException("error is not available unless state is Error.");
}
return _error;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="Future{T}"/> class.
/// </summary>
public Future()
{
_state = FutureState.Pending;
}
public IFuture<T> OnItem(FutureValueCallback<T> callback)
{
if (_state < FutureState.Success && !_itemCallbacks.Contains(callback))
_itemCallbacks.Add(callback);
return this;
}
/// <summary>
/// Adds a new callback to invoke if the future value is retrieved successfully.
/// </summary>
/// <param name="callback">The callback to invoke.</param>
/// <returns>The future so additional calls can be chained together.</returns>
public IFuture<T> OnSuccess(FutureValueCallback<T> callback)
{
if (_state == FutureState.Success)
{
callback(this.value);
}
else if (_state != FutureState.Error && !_successCallbacks.Contains(callback))
{
_successCallbacks.Add(callback);
}
return this;
}
/// <summary>
/// Adds a new callback to invoke if the future has an error.
/// </summary>
/// <param name="callback">The callback to invoke.</param>
/// <returns>The future so additional calls can be chained together.</returns>
public IFuture<T> OnError(FutureErrorCallback callback)
{
if (_state == FutureState.Error)
{
callback(this.error);
}
else if (_state != FutureState.Success && !_errorCallbacks.Contains(callback))
{
_errorCallbacks.Add(callback);
}
return this;
}
/// <summary>
/// Adds a new callback to invoke if the future value is retrieved successfully or has an error.
/// </summary>
/// <param name="callback">The callback to invoke.</param>
/// <returns>The future so additional calls can be chained together.</returns>
public IFuture<T> OnComplete(FutureCallback<T> callback)
{
if (_state == FutureState.Success || _state == FutureState.Error)
{
callback(this);
}
else
{
if (!_complationCallbacks.Contains(callback))
_complationCallbacks.Add(callback);
}
return this;
}
#pragma warning disable 1998
/// <summary>
/// Begins running a given function on a background thread to resolve the future's value, as long
/// as it is still in the Pending state.
/// </summary>
/// <param name="func">The function that will retrieve the desired value.</param>
public IFuture<T> Process(Func<T> func)
{
if (_state != FutureState.Pending)
{
throw new InvalidOperationException("Cannot process a future that isn't in the Pending state.");
}
BeginProcess();
_processFunc = func;
#if NETFX_CORE
#pragma warning disable 4014
Windows.System.Threading.ThreadPool.RunAsync(ThreadFunc);
#pragma warning restore 4014
#else
System.Threading.ThreadPool.QueueUserWorkItem(ThreadFunc);
#endif
return this;
}
private
#if NETFX_CORE
async
#endif
void ThreadFunc(object param)
{
try
{
// Directly call the Impl version to avoid the state validation of the public method
AssignImpl(_processFunc());
}
catch (Exception e)
{
// Directly call the Impl version to avoid the state validation of the public method
FailImpl(e);
}
finally
{
_processFunc = null;
}
}
#pragma warning restore 1998
/// <summary>
/// Allows manually assigning a value to a future, as long as it is still in the pending state.
/// </summary>
/// <remarks>
/// There are times where you may not need to do background processing for a value. For example,
/// you may have a cache of values and can just hand one out. In those cases you still want to
/// return a future for the method signature, but can just call this method to fill in the future.
/// </remarks>
/// <param name="value">The value to assign the future.</param>
public void Assign(T value)
{
if (_state != FutureState.Pending && _state != FutureState.Processing)
{
throw new InvalidOperationException("Cannot assign a value to a future that isn't in the Pending or Processing state.");
}
AssignImpl(value);
}
public void BeginProcess(T initialItem = default(T))
{
_state = FutureState.Processing;
_value = initialItem;
}
public void AssignItem(T value)
{
_value = value;
_error = null;
foreach (var callback in _itemCallbacks)
callback(this.value);
}
public void Finish()
{
_state = FutureState.Success;
FlushSuccessCallbacks();
}
/// <summary>
/// Allows manually failing a future, as long as it is still in the pending state.
/// </summary>
/// <remarks>
/// As with the Assign method, there are times where you may know a future value is a failure without
/// doing any background work. In those cases you can simply fail the future manually and return it.
/// </remarks>
/// <param name="error">The exception to use to fail the future.</param>
public void Fail(Exception error)
{
if (_state != FutureState.Pending && _state != FutureState.Processing)
{
throw new InvalidOperationException("Cannot fail future that isn't in the Pending or Processing state.");
}
FailImpl(error);
}
private void AssignImpl(T value)
{
_value = value;
_error = null;
_state = FutureState.Success;
FlushSuccessCallbacks();
}
private void FailImpl(Exception error)
{
_value = default(T);
_error = error;
_state = FutureState.Error;
FlushErrorCallbacks();
}
private void FlushSuccessCallbacks()
{
foreach (var callback in _successCallbacks)
callback(this.value);
FlushComplationCallbacks();
}
private void FlushErrorCallbacks()
{
foreach (var callback in _errorCallbacks)
callback(this.error);
FlushComplationCallbacks();
}
private void FlushComplationCallbacks()
{
foreach (var callback in _complationCallbacks)
callback(this);
ClearCallbacks();
}
private void ClearCallbacks()
{
_itemCallbacks.Clear();
_successCallbacks.Clear();
_errorCallbacks.Clear();
_complationCallbacks.Clear();
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: a9009e6f5e86bd5459d443846e42be9e
timeCreated: 1515175777
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,267 @@
#if CSHARP_7_OR_LATER
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using BestHTTP.Logger;
using UnityEngine;
namespace BestHTTP
{
public sealed class AsyncHTTPException : Exception
{
/// <summary>
/// Status code of the server's response.
/// </summary>
public int StatusCode;
/// <summary>
/// Content sent by the server.
/// </summary>
public string Content;
public AsyncHTTPException(string message)
: base(message)
{
}
public AsyncHTTPException(string message, Exception innerException)
: base(message, innerException)
{
}
public AsyncHTTPException(int statusCode, string message, string content)
:base(message)
{
this.StatusCode = statusCode;
this.Content = content;
}
public override string ToString()
{
return string.Format("StatusCode: {0}, Message: {1}, Content: {2}, StackTrace: {3}", this.StatusCode, this.Message, this.Content, this.StackTrace);
}
}
public static class HTTPRequestAsyncExtensions
{
public static Task<HTTPResponse> GetHTTPResponseAsync(this HTTPRequest request, CancellationToken token = default)
{
return CreateTask<HTTPResponse>(request, token, (req, resp, tcs) =>
{
switch (req.State)
{
// The request finished without any problem.
case HTTPRequestStates.Finished:
tcs.TrySetResult(resp);
break;
// The request finished with an unexpected error. The request's Exception property may contain more info about the error.
case HTTPRequestStates.Error:
VerboseLogging(request, "Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception"));
tcs.TrySetException(CreateException("No Exception", null, req.Exception));
break;
// The request aborted, initiated by the user.
case HTTPRequestStates.Aborted:
VerboseLogging(request, "Request Aborted!");
tcs.TrySetCanceled();
break;
// Connecting to the server is timed out.
case HTTPRequestStates.ConnectionTimedOut:
VerboseLogging(request, "Connection Timed Out!");
tcs.TrySetException(CreateException("Connection Timed Out!"));
break;
// The request didn't finished in the given time.
case HTTPRequestStates.TimedOut:
VerboseLogging(request, "Processing the request Timed Out!");
tcs.TrySetException(CreateException("Processing the request Timed Out!"));
break;
}
});
}
public static Task<string> GetAsStringAsync(this HTTPRequest request, CancellationToken token = default)
{
return CreateTask<string>(request, token, (req, resp, tcs) =>
{
switch (req.State)
{
// The request finished without any problem.
case HTTPRequestStates.Finished:
if (resp.IsSuccess)
tcs.TrySetResult(resp.DataAsText);
else
tcs.TrySetException(CreateException("Request finished Successfully, but the server sent an error.", resp));
break;
// The request finished with an unexpected error. The request's Exception property may contain more info about the error.
case HTTPRequestStates.Error:
VerboseLogging(request, "Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception"));
tcs.TrySetException(CreateException("No Exception", null, req.Exception));
break;
// The request aborted, initiated by the user.
case HTTPRequestStates.Aborted:
VerboseLogging(request, "Request Aborted!");
tcs.TrySetCanceled();
break;
// Connecting to the server is timed out.
case HTTPRequestStates.ConnectionTimedOut:
VerboseLogging(request, "Connection Timed Out!");
tcs.TrySetException(CreateException("Connection Timed Out!"));
break;
// The request didn't finished in the given time.
case HTTPRequestStates.TimedOut:
VerboseLogging(request, "Processing the request Timed Out!");
tcs.TrySetException(CreateException("Processing the request Timed Out!"));
break;
}
});
}
public static Task<Texture2D> GetAsTexture2DAsync(this HTTPRequest request, CancellationToken token = default)
{
return CreateTask<Texture2D>(request, token, (req, resp, tcs) =>
{
switch (req.State)
{
// The request finished without any problem.
case HTTPRequestStates.Finished:
if (resp.IsSuccess)
tcs.TrySetResult(resp.DataAsTexture2D);
else
tcs.TrySetException(CreateException("Request finished Successfully, but the server sent an error.", resp));
break;
// The request finished with an unexpected error. The request's Exception property may contain more info about the error.
case HTTPRequestStates.Error:
VerboseLogging(request, "Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception"));
tcs.TrySetException(CreateException("No Exception", null, req.Exception));
break;
// The request aborted, initiated by the user.
case HTTPRequestStates.Aborted:
VerboseLogging(request, "Request Aborted!");
tcs.TrySetCanceled();
break;
// Connecting to the server is timed out.
case HTTPRequestStates.ConnectionTimedOut:
VerboseLogging(request, "Connection Timed Out!");
tcs.TrySetException(CreateException("Connection Timed Out!"));
break;
// The request didn't finished in the given time.
case HTTPRequestStates.TimedOut:
VerboseLogging(request, "Processing the request Timed Out!");
tcs.TrySetException(CreateException("Processing the request Timed Out!"));
break;
}
});
}
public static Task<byte[]> GetRawDataAsync(this HTTPRequest request, CancellationToken token = default)
{
return CreateTask<byte[]>(request, token, (req, resp, tcs) =>
{
switch (req.State)
{
// The request finished without any problem.
case HTTPRequestStates.Finished:
if (resp.IsSuccess)
tcs.TrySetResult(resp.Data);
else
tcs.TrySetException(CreateException("Request finished Successfully, but the server sent an error.", resp));
break;
// The request finished with an unexpected error. The request's Exception property may contain more info about the error.
case HTTPRequestStates.Error:
VerboseLogging(request, "Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception"));
tcs.TrySetException(CreateException("No Exception", null, req.Exception));
break;
// The request aborted, initiated by the user.
case HTTPRequestStates.Aborted:
VerboseLogging(request, "Request Aborted!");
tcs.TrySetCanceled();
break;
// Connecting to the server is timed out.
case HTTPRequestStates.ConnectionTimedOut:
VerboseLogging(request, "Connection Timed Out!");
tcs.TrySetException(CreateException("Connection Timed Out!"));
break;
// The request didn't finished in the given time.
case HTTPRequestStates.TimedOut:
VerboseLogging(request, "Processing the request Timed Out!");
tcs.TrySetException(CreateException("Processing the request Timed Out!"));
break;
}
});
}
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public static Task<T> CreateTask<T>(HTTPRequest request, CancellationToken token, Action<HTTPRequest, HTTPResponse, TaskCompletionSource<T>> callback)
{
HTTPManager.Setup();
var tcs = new TaskCompletionSource<T>();
request.Callback = (req, resp) =>
{
if (token.IsCancellationRequested)
tcs.SetCanceled();
else
callback(req, resp, tcs);
};
if (token.CanBeCanceled)
token.Register((state) => (state as HTTPRequest)?.Abort(), request);
if (request.State == HTTPRequestStates.Initial)
request.Send();
return tcs.Task;
}
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public static void VerboseLogging(HTTPRequest request, string str)
{
HTTPManager.Logger.Verbose("HTTPRequestAsyncExtensions", str, request.Context);
}
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public static Exception CreateException(string errorMessage, HTTPResponse resp = null, Exception ex = null)
{
if (resp != null)
return new AsyncHTTPException(resp.StatusCode, resp.Message, resp.DataAsText);
else if (ex != null)
return new AsyncHTTPException(ex.Message, ex);
else
return new AsyncHTTPException(errorMessage);
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 875eda53b9b14784484c59463d205081
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,10 @@
fileFormatVersion: 2
guid: 40ea771765c2ae549abc4c264e500b76
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,133 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BestHTTP.PlatformSupport.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)
{
// According to https://tools.ietf.org/html/rfc7234#section-5.2.1.1
// Max-Age has a form "max-age=5", but some (imgur.com specifically) sends it as "max-age:5"
string key = headerStr.Read(ref pos, (ch) => ch != ';' && ch != '=' && ch != ':' && ch != ',', true);
this.Key = key;
char? skippedChar = headerStr.Peek(pos - 1);
bool isValue = skippedChar == '=' || skippedChar == ':';
bool isOption = isOptionIsAnOption && skippedChar == ';';
while ((skippedChar != null && isValue || isOption) && pos < headerStr.Length)
{
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 (this.Options != null && this.Options.Count > 0)
{
StringBuilder sb = StringBuilderPool.Get(4); //new StringBuilder(4);
sb.Append(Key);
sb.Append("=");
sb.Append(Value);
foreach(var option in Options)
{
sb.Append(";");
sb.Append(option.ToString());
}
return StringBuilderPool.ReleaseAndGrab(sb);
}
else if (!string.IsNullOrEmpty(Value))
return Key + '=' + Value;
else
return Key;
}
#endregion
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 1465d4183951bf64dad0047b7c6e7243
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
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 ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
private List<IHeartbeat> Heartbeats = new List<IHeartbeat>();
private IHeartbeat[] UpdateArray;
private DateTime LastUpdate = DateTime.MinValue;
public void Subscribe(IHeartbeat heartbeat)
{
rwLock.EnterWriteLock();
try
{
if (!Heartbeats.Contains(heartbeat))
Heartbeats.Add(heartbeat);
}
finally
{
rwLock.ExitWriteLock();
}
}
public void Unsubscribe(IHeartbeat heartbeat)
{
rwLock.EnterWriteLock();
try
{
Heartbeats.Remove(heartbeat);
}
finally
{
rwLock.ExitWriteLock();
}
}
public void Update()
{
if (LastUpdate == DateTime.MinValue)
LastUpdate = DateTime.UtcNow;
else
{
TimeSpan dif = DateTime.UtcNow - LastUpdate;
LastUpdate = DateTime.UtcNow;
int count = 0;
rwLock.EnterReadLock();
try
{
if (UpdateArray == null || UpdateArray.Length < Heartbeats.Count)
Array.Resize(ref UpdateArray, Heartbeats.Count);
Heartbeats.CopyTo(0, UpdateArray, 0, Heartbeats.Count);
count = Heartbeats.Count;
}
finally
{
rwLock.ExitReadLock();
}
for (int i = 0; i < count; ++i)
{
try
{
UpdateArray[i].OnHeartbeatUpdate(dif);
}
catch
{ }
}
}
}
public void Clear()
{
rwLock.EnterWriteLock();
try
{
Heartbeats.Clear();
}
finally
{
rwLock.ExitWriteLock();
}
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 6c610eeb580ec8e4c90789edb631cc76
MonoImporter:
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,10 @@
fileFormatVersion: 2
guid: 834cc34ef3418894589ef2e8389ff8c0
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,32 @@
namespace BestHTTP.Extensions
{
public sealed class PeekableIncomingSegmentStream : BufferSegmentStream
{
private int peek_listIdx;
private int peek_pos;
public void BeginPeek()
{
peek_listIdx = 0;
peek_pos = base.bufferList.Count > 0 ? base.bufferList[0].Offset : 0;
}
public int PeekByte()
{
if (base.bufferList.Count == 0)
return -1;
var segment = base.bufferList[this.peek_listIdx];
if (peek_pos >= segment.Offset + segment.Count)
{
if (base.bufferList.Count <= this.peek_listIdx + 1)
return -1;
segment = base.bufferList[++this.peek_listIdx];
this.peek_pos = segment.Offset;
}
return segment.Data[this.peek_pos++];
}
}
}

View File

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

View File

@@ -0,0 +1,144 @@
using BestHTTP.PlatformSupport.Memory;
using System;
using System.IO;
namespace BestHTTP.Extensions
{
public sealed class ReadOnlyBufferedStream : Stream
{
Stream stream;
public const int READBUFFER = 8192;
byte[] buf;
int available = 0;
int pos = 0;
public ReadOnlyBufferedStream(Stream nstream)
:this(nstream, READBUFFER)
{
}
public ReadOnlyBufferedStream(Stream nstream, int bufferSize)
{
stream = nstream;
buf = BufferPool.Get(bufferSize, true);
}
public override int Read(byte[] buffer, int offset, int size)
{
if (available > 0)
{
// copy & return
int copyCount = Math.Min(available, size);
Array.Copy(buf, pos, buffer, offset, copyCount);
pos += copyCount;
available -= copyCount;
return copyCount;
}
else
{
if (size >= buf.Length)
{
// read directly to buffer
return stream.Read(buffer, offset, size);
}
else
{
// read to buf and copy
pos = 0;
available = stream.Read(buf, 0, buf.Length);
if (available > 0)
return Read(buffer, offset, size);
else
return 0;
}
}
}
public override int ReadByte()
{
if (available > 0)
{
available -= 1;
pos += 1;
return buf[pos - 1];
}
else
{
try
{
available = stream.Read(buf, 0, buf.Length);
pos = 0;
}
catch
{
return -1;
}
if (available < 1)
{
return -1;
}
else
{
available -= 1;
pos += 1;
return buf[pos - 1];
}
}
}
protected override void Dispose(bool disposing)
{
if (disposing && buf != null)
BufferPool.Release(buf);
buf = null;
}
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { throw new NotImplementedException(); }
}
public override bool CanWrite
{
get { throw new NotImplementedException(); }
}
public override long Length
{
get { throw new NotImplementedException(); }
}
public override long Position
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public override void Flush()
{
throw new NotImplementedException();
}
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();
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e6b9a4ff6813b4f4b9f2ffa453c3fccc
timeCreated: 1543513046
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,152 @@
using BestHTTP.PlatformSupport.Memory;
using System;
namespace BestHTTP.Extensions
{
/// <summary>
/// Wrapper of multiple streams. Writes and reads are both supported. Read goes trough all the streams.
/// </summary>
public 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)
{
// Dispose previous stream
try
{
Streams[CurrentIdx - 1].Dispose();
Streams[CurrentIdx - 1] = null;
}
catch (Exception ex)
{
HTTPManager.Logger.Exception("StreamList", "Dispose", ex);
}
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)
{
var buffer = str.GetASCIIBytes();
this.Write(buffer.Data, buffer.Offset, buffer.Count);
BufferPool.Release(buffer);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
for (int i = 0; i < Streams.Length; ++i)
if (Streams[i] != null)
{
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");
}
}
}

View File

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

View File

@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
namespace BestHTTP.Extensions
{
public
#if CSHARP_7_OR_LATER
readonly
#endif
struct TimerData
{
public readonly DateTime Created;
public readonly TimeSpan Interval;
public readonly object Context;
public readonly Func<DateTime, object, bool> OnTimer;
public bool IsOnTime(DateTime now)
{
return now >= this.Created + this.Interval;
}
public TimerData(TimeSpan interval, object context, Func<DateTime, object, bool> onTimer)
{
this.Created = DateTime.Now;
this.Interval = interval;
this.Context = context;
this.OnTimer = onTimer;
}
/// <summary>
/// Create a new TimerData but the Created field will be set to the current time.
/// </summary>
public TimerData CreateNew()
{
return new TimerData(this.Interval, this.Context, this.OnTimer);
}
public override string ToString()
{
return string.Format("[TimerData Created: {0}, Interval: {1}, IsOnTime: {2}]", this.Created.ToString(System.Globalization.CultureInfo.InvariantCulture), this.Interval, this.IsOnTime(DateTime.Now));
}
}
public static class Timer
{
private static List<TimerData> Timers = new List<TimerData>();
public static void Add(TimerData timer)
{
Timers.Add(timer);
}
internal static void Process()
{
if (Timers.Count == 0)
return;
DateTime now = DateTime.Now;
for (int i = 0; i < Timers.Count; ++i)
{
TimerData timer = Timers[i];
if (timer.IsOnTime(now))
{
bool repeat = timer.OnTimer(now, timer.Context);
if (repeat)
Timers[i] = timer.CreateNew();
else
Timers.RemoveAt(i--);
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 6b79070540cbf6f42b89c74c2fb978f5
timeCreated: 1571210041
licenseType: Store
MonoImporter:
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,10 @@
fileFormatVersion: 2
guid: 4cba91161fe643c419eedafefdf1f959
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,82 @@
using BestHTTP.PlatformSupport.Memory;
using System;
using System.IO;
namespace BestHTTP.Extensions
{
/// <summary>
/// A custom buffer stream implementation that will not close the underlying stream.
/// </summary>
public sealed class WriteOnlyBufferedStream : Stream
{
public override bool CanRead { get { return false; } }
public override bool CanSeek { get { return false; } }
public override bool CanWrite { get { return true; } }
public override long Length { get { return this.buffer.Length; } }
public override long Position { get { return this._position; } set { throw new NotImplementedException("Position set"); } }
private int _position;
private byte[] buffer;
private Stream stream;
public WriteOnlyBufferedStream(Stream stream, int bufferSize)
{
this.stream = stream;
this.buffer = BufferPool.Get(bufferSize, true);
this._position = 0;
}
public override void Flush()
{
if (this._position > 0)
{
this.stream.Write(this.buffer, 0, this._position);
this.stream.Flush();
//if (HTTPManager.Logger.Level == Logger.Loglevels.All)
// HTTPManager.Logger.Information("WriteOnlyBufferedStream", string.Format("Flushed {0:N0} bytes", this._position));
this._position = 0;
}
}
public override void Write(byte[] bufferFrom, int offset, int count)
{
while (count > 0)
{
int writeCount = Math.Min(count, this.buffer.Length - this._position);
Array.Copy(bufferFrom, offset, this.buffer, this._position, writeCount);
this._position += writeCount;
offset += writeCount;
count -= writeCount;
if (this._position == this.buffer.Length)
this.Flush();
}
}
public override int Read(byte[] buffer, int offset, int count)
{
return 0;
}
public override long Seek(long offset, SeekOrigin origin)
{
return 0;
}
public override void SetLength(long value) { }
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing && this.buffer != null)
BufferPool.Release(this.buffer);
this.buffer = null;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2df120a767773ee478acf872b86ae326
timeCreated: 1530027551
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: