EonaCat.Network/EonaCat.Network/System/Sockets/Web/Extensions.cs

1636 lines
47 KiB
C#

// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.IO.Compression;
using System.Net.Sockets;
using System.Text;
namespace EonaCat.Network
{
public static class Ext
{
private const string _tspecials = "()<>@,;:\\\"/[]?={} \t";
private const int BUFFER_SIZE = 1024;
private static readonly byte[] _last = new byte[] { 0x00 };
private static readonly int _retry = 5;
public static bool Contains(this string value, params char[] chars)
{
return chars == null || chars.Length == 0
|| value != null && value.Length != 0
&& value.IndexOfAny(chars) > -1;
}
public static bool Contains(this NameValueCollection collection, string name)
{
return collection != null && collection.Count > 0 && collection[name] != null;
}
public static bool Contains(this NameValueCollection collection, string name, string value)
{
if (collection == null || collection.Count == 0)
{
return false;
}
var vals = collection[name];
if (vals == null)
{
return false;
}
foreach (var val in vals.Split(','))
{
if (val.Trim().Equals(value, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
public static void Emit(this EventHandler eventHandler, object sender, EventArgs e)
{
eventHandler?.Invoke(sender, e);
}
public static void Emit<TEventArgs>(
this EventHandler<TEventArgs> eventHandler, object sender, TEventArgs e)
where TEventArgs : EventArgs
{
eventHandler?.Invoke(sender, e);
}
public static CookieCollection GetCookies(this NameValueCollection headers, bool response)
{
var name = response ? "Set-Cookie" : "Cookie";
return headers != null && headers.Contains(name)
? CookieCollection.Parse(headers[name], response)
: new CookieCollection();
}
public static string GetDescription(this HttpStatusCode code)
{
return ((int)code).GetStatusDescription();
}
public static string GetStatusDescription(this int code)
{
switch (code)
{
case 100: return "Continue";
case 101: return "Switching Protocols";
case 102: return "Processing";
case 200: return "OK";
case 201: return "Created";
case 202: return "Accepted";
case 203: return "Non-Authoritative Information";
case 204: return "No Content";
case 205: return "Reset Content";
case 206: return "Partial Content";
case 207: return "Multi-Status";
case 300: return "Multiple Choices";
case 301: return "Moved Permanently";
case 302: return "Found";
case 303: return "See Other";
case 304: return "Not Modified";
case 305: return "Use Proxy";
case 307: return "Temporary Redirect";
case 400: return "Bad Request";
case 401: return "Unauthorized";
case 402: return "Payment Required";
case 403: return "Forbidden";
case 404: return "Not Found";
case 405: return "Method Not Allowed";
case 406: return "Not Acceptable";
case 407: return "Proxy Authentication Required";
case 408: return "Request Timeout";
case 409: return "Conflict";
case 410: return "Gone";
case 411: return "Length Required";
case 412: return "Precondition Failed";
case 413: return "Request Entity Too Large";
case 414: return "Request-Uri Too Long";
case 415: return "Unsupported Media Type";
case 416: return "Requested Range Not Satisfiable";
case 417: return "Expectation Failed";
case 422: return "Unprocessable Entity";
case 423: return "Locked";
case 424: return "Failed Dependency";
case 500: return "Internal Server Error";
case 501: return "Not Implemented";
case 502: return "Bad Gateway";
case 503: return "Service Unavailable";
case 504: return "Gateway Timeout";
case 505: return "Http Version Not Supported";
case 507: return "Insufficient Storage";
}
return string.Empty;
}
public static bool IsCloseStatusCode(this ushort value)
{
return value > 999 && value < 5000;
}
public static bool IsEnclosedIn(this string value, char c)
{
return value != null
&& value.Length > 1
&& value[0] == c
&& value[value.Length - 1] == c;
}
public static bool IsHostOrder(this ByteOrder order)
{
// true: !(true ^ true) or !(false ^ false)
// false: !(true ^ false) or !(false ^ true)
return !(BitConverter.IsLittleEndian ^ (order == ByteOrder.Little));
}
/// <summary>
/// An extension method to determine if an IP address is internal
/// Class A Private IP Range: 10.0.0.0 ? 10.255.255.255
/// Class B Private IP Range: 172.16.0.0 ? 172.31.255.255
/// Class C Private IP Range: 192.168.0.0 ? 192.168.255.25
/// </summary>
/// <param name="address">The IP address that will be tested</param>
/// <returns>Returns true if the IP is internal, false if it is external</returns>
public static bool IsInternal(this System.Net.IPAddress address)
{
byte[] bytes = address.GetAddressBytes();
switch (bytes[0])
{
case 10:
return true;
case 172:
return bytes[1] < 32 && bytes[1] >= 16;
case 192:
return bytes[1] == 168;
default:
return false;
}
}
public static bool IsLocal(this System.Net.IPAddress address)
{
if (address == null)
{
return false;
}
if (address.Equals(System.Net.IPAddress.Any))
{
return true;
}
if (address.Equals(System.Net.IPAddress.Loopback))
{
return true;
}
if (Socket.OSSupportsIPv6)
{
if (address.Equals(System.Net.IPAddress.IPv6Any))
{
return true;
}
if (address.Equals(System.Net.IPAddress.IPv6Loopback))
{
return true;
}
}
if (address.IsInternal())
return true;
var host = System.Net.Dns.GetHostName();
var addrs = System.Net.Dns.GetHostAddresses(host);
foreach (var addr in addrs)
{
if (address.Equals(addr))
{
return true;
}
}
return false;
}
public static bool IsNullOrEmpty(this string value)
{
return value == null || value.Length == 0;
}
public static bool IsPredefinedScheme(this string value)
{
if (value == null || value.Length < 2)
{
return false;
}
var c = value[0];
if (c == 'h')
{
return value == "http" || value == "https";
}
if (c == 'w')
{
return value == "ws" || value == "wss";
}
if (c == 'f')
{
return value == "file" || value == "ftp";
}
if (c == 'g')
{
return value == "gopher";
}
if (c == 'm')
{
return value == "mailto";
}
if (c == 'n')
{
c = value[1];
return c == 'e'
? value == "news" || value == "net.pipe" || value == "net.tcp"
: value == "nntp";
}
return false;
}
public static bool IsUpgradeTo(this HttpListenerRequest request, string protocol)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (protocol == null)
{
throw new ArgumentNullException(nameof(protocol));
}
if (protocol.Length == 0)
{
throw new ArgumentException("An empty string.", nameof(protocol));
}
return request.Headers.Contains("Upgrade", protocol) &&
request.Headers.Contains("Connection", "Upgrade");
}
public static bool MaybeUri(this string value)
{
if (value == null || value.Length == 0)
{
return false;
}
var idx = value.IndexOf(':');
if (idx == -1)
{
return false;
}
if (idx >= 10)
{
return false;
}
var schm = value.Substring(0, idx);
return schm.IsPredefinedScheme();
}
public static T[] SubArray<T>(this T[] array, int startIndex, int length)
{
int len;
if (array == null || (len = array.Length) == 0)
{
return new T[0];
}
if (startIndex < 0 || length <= 0 || startIndex + length > len)
{
return new T[0];
}
if (startIndex == 0 && length == len)
{
return array;
}
var subArray = new T[length];
Array.Copy(array, startIndex, subArray, 0, length);
return subArray;
}
public static T[] SubArray<T>(this T[] array, long startIndex, long length)
{
long len;
if (array == null || (len = array.LongLength) == 0)
{
return new T[0];
}
if (startIndex < 0 || length <= 0 || startIndex + length > len)
{
return new T[0];
}
if (startIndex == 0 && length == len)
{
return array;
}
var subArray = new T[length];
Array.Copy(array, startIndex, subArray, 0, length);
return subArray;
}
public static void Times(this int n, Action action)
{
if (n > 0 && action != null)
{
((ulong)n).times(action);
}
}
public static void Times(this long n, Action action)
{
if (n > 0 && action != null)
{
((ulong)n).times(action);
}
}
public static void Times(this uint n, Action action)
{
if (n > 0 && action != null)
{
((ulong)n).times(action);
}
}
public static void Times(this ulong n, Action action)
{
if (n > 0 && action != null)
{
n.times(action);
}
}
public static void Times(this int n, Action<int> action)
{
if (n > 0 && action != null)
{
for (int i = 0; i < n; i++)
{
action(i);
}
}
}
public static void Times(this long n, Action<long> action)
{
if (n > 0 && action != null)
{
for (long i = 0; i < n; i++)
{
action(i);
}
}
}
public static void Times(this uint n, Action<uint> action)
{
if (n > 0 && action != null)
{
for (uint i = 0; i < n; i++)
{
action(i);
}
}
}
public static void Times(this ulong n, Action<ulong> action)
{
if (n > 0 && action != null)
{
for (ulong i = 0; i < n; i++)
{
action(i);
}
}
}
public static T To<T>(this byte[] source, ByteOrder sourceOrder)
where T : struct
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (source.Length == 0)
{
return default(T);
}
var type = typeof(T);
var buff = source.ToHostOrder(sourceOrder);
return type == typeof(bool)
? (T)(object)BitConverter.ToBoolean(buff, 0)
: type == typeof(char)
? (T)(object)BitConverter.ToChar(buff, 0)
: type == typeof(double)
? (T)(object)BitConverter.ToDouble(buff, 0)
: type == typeof(short)
? (T)(object)BitConverter.ToInt16(buff, 0)
: type == typeof(int)
? (T)(object)BitConverter.ToInt32(buff, 0)
: type == typeof(long)
? (T)(object)BitConverter.ToInt64(buff, 0)
: type == typeof(float)
? (T)(object)BitConverter.ToSingle(buff, 0)
: type == typeof(ushort)
? (T)(object)BitConverter.ToUInt16(buff, 0)
: type == typeof(uint)
? (T)(object)BitConverter.ToUInt32(buff, 0)
: type == typeof(ulong)
? (T)(object)BitConverter.ToUInt64(buff, 0)
: default(T);
}
public static byte[] ToByteArray<T>(this T value, ByteOrder order)
where T : struct
{
var type = typeof(T);
var bytes = type == typeof(bool)
? BitConverter.GetBytes((bool)(object)value)
: type == typeof(byte)
? new byte[] { (byte)(object)value }
: type == typeof(char)
? BitConverter.GetBytes((char)(object)value)
: type == typeof(double)
? BitConverter.GetBytes((double)(object)value)
: type == typeof(short)
? BitConverter.GetBytes((short)(object)value)
: type == typeof(int)
? BitConverter.GetBytes((int)(object)value)
: type == typeof(long)
? BitConverter.GetBytes((long)(object)value)
: type == typeof(float)
? BitConverter.GetBytes((float)(object)value)
: type == typeof(ushort)
? BitConverter.GetBytes((ushort)(object)value)
: type == typeof(uint)
? BitConverter.GetBytes((uint)(object)value)
: type == typeof(ulong)
? BitConverter.GetBytes((ulong)(object)value)
: WSClient.EmptyBytes;
if (bytes.Length > 1 && !order.IsHostOrder())
{
Array.Reverse(bytes);
}
return bytes;
}
public static byte[] ToHostOrder(this byte[] source, ByteOrder sourceOrder)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
return source.Length > 1 && !sourceOrder.IsHostOrder() ? source.Reverse() : source;
}
public static string ToString<T>(this T[] array, string separator)
{
if (array == null)
{
throw new ArgumentNullException(nameof(array));
}
var len = array.Length;
if (len == 0)
{
return string.Empty;
}
separator ??= string.Empty;
var buff = new StringBuilder(64);
(len - 1).Times(i => buff.AppendFormat("{0}{1}", array[i].ToString(), separator));
buff.Append(array[len - 1].ToString());
return buff.ToString();
}
public static Uri ToUri(this string value)
{
Uri.TryCreate(
value, value.MaybeUri() ? UriKind.Absolute : UriKind.Relative, out Uri ret
);
return ret;
}
public static string UrlDecode(this string value)
{
return value != null && value.Length > 0
? HttpUtility.UrlDecode(value)
: value;
}
public static string UrlEncode(this string value)
{
return value != null && value.Length > 0
? HttpUtility.UrlEncode(value)
: value;
}
public static void WriteContent(this HttpListenerResponse response, byte[] content)
{
if (response == null)
{
throw new ArgumentNullException(nameof(response));
}
if (content == null)
{
throw new ArgumentNullException(nameof(content));
}
var len = content.LongLength;
if (len == 0)
{
response.Close();
return;
}
response.ContentLength64 = len;
var output = response.OutputStream;
if (len <= int.MaxValue)
{
output.Write(content, 0, (int)len);
}
else
{
output.WriteBytes(content, BUFFER_SIZE);
}
output.Close();
}
internal static byte[] Append(this ushort code, string reason)
{
var ret = code.InternalToByteArray(ByteOrder.Big);
if (reason != null && reason.Length > 0)
{
var buff = new List<byte>(ret);
buff.AddRange(Encoding.UTF8.GetBytes(reason));
ret = buff.ToArray();
}
return ret;
}
internal static void Close(this HttpListenerResponse response, HttpStatusCode code)
{
response.StatusCode = (int)code;
response.OutputStream.Close();
}
internal static void CloseWithAuthChallenge(
this HttpListenerResponse response, string challenge)
{
response.Headers.InternalSet("WWW-Authenticate", challenge, true);
response.Close(HttpStatusCode.Unauthorized);
}
internal static byte[] Compress(this byte[] data, CompressionMethod method)
{
return method == CompressionMethod.Deflate
? data.compress()
: data;
}
internal static Stream Compress(this Stream stream, CompressionMethod method)
{
return method == CompressionMethod.Deflate
? stream.compress()
: stream;
}
internal static byte[] CompressToArray(this Stream stream, CompressionMethod method)
{
return method == CompressionMethod.Deflate
? stream.compressToArray()
: stream.ToByteArray();
}
internal static bool Contains<T>(
this IEnumerable<T> source, Func<T, bool> condition
)
{
foreach (T elm in source)
{
if (condition(elm))
{
return true;
}
}
return false;
}
internal static bool ContainsTwice(this string[] values)
{
var len = values.Length;
var end = len - 1;
Func<int, bool> seek = null;
seek = idx =>
{
if (idx == end)
{
return false;
}
var val = values[idx];
for (var i = idx + 1; i < len; i++)
{
if (values[i] == val)
{
return true;
}
}
return seek(++idx);
};
return seek(0);
}
internal static T[] Copy<T>(this T[] source, int length)
{
var dest = new T[length];
Array.Copy(source, 0, dest, 0, length);
return dest;
}
internal static T[] Copy<T>(this T[] source, long length)
{
var dest = new T[length];
Array.Copy(source, 0, dest, 0, length);
return dest;
}
internal static void CopyTo(this Stream source, Stream destination, int bufferLength)
{
var buff = new byte[bufferLength];
var nread = 0;
while ((nread = source.Read(buff, 0, bufferLength)) > 0)
{
destination.Write(buff, 0, nread);
}
}
internal static void CopyToAsync(
this Stream source,
Stream destination,
int bufferLength,
Action completed,
Action<Exception> error)
{
var buff = new byte[bufferLength];
AsyncCallback callback = null;
callback = ar =>
{
try
{
var nread = source.EndRead(ar);
if (nread <= 0)
{
completed?.Invoke();
return;
}
destination.Write(buff, 0, nread);
source.BeginRead(buff, 0, bufferLength, callback, null);
}
catch (Exception ex)
{
error?.Invoke(ex);
}
};
try
{
source.BeginRead(buff, 0, bufferLength, callback, null);
}
catch (Exception ex)
{
error?.Invoke(ex);
}
}
internal static byte[] Decompress(this byte[] data, CompressionMethod method)
{
return method == CompressionMethod.Deflate
? data.decompress()
: data;
}
internal static Stream Decompress(this Stream stream, CompressionMethod method)
{
return method == CompressionMethod.Deflate
? stream.decompress()
: stream;
}
internal static byte[] DecompressToArray(this Stream stream, CompressionMethod method)
{
return method == CompressionMethod.Deflate
? stream.decompressToArray()
: stream.ToByteArray();
}
internal static bool EqualsWith(this int value, char c, Action<int> action)
{
action(value);
return value == c - 0;
}
internal static string GetAbsolutePath(this Uri uri)
{
if (uri.IsAbsoluteUri)
{
return uri.AbsolutePath;
}
var original = uri.OriginalString;
if (original[0] != '/')
{
return null;
}
var idx = original.IndexOfAny(new[] { '?', '#' });
return idx > 0 ? original.Substring(0, idx) : original;
}
internal static string GetDnsSafeHost(this Uri uri, bool bracketIPv6)
{
return bracketIPv6 && uri.HostNameType == UriHostNameType.IPv6
? uri.Host
: uri.DnsSafeHost;
}
internal static string GetMessage(this CloseStatusCode code)
{
return code == CloseStatusCode.ProtocolError
? "A WebSocket protocol error has occurred."
: code == CloseStatusCode.UnsupportedData
? "Unsupported data has been received."
: code == CloseStatusCode.Abnormal
? "An exception has occurred."
: code == CloseStatusCode.InvalidData
? "Invalid data has been received."
: code == CloseStatusCode.PolicyViolation
? "A policy violation has occurred."
: code == CloseStatusCode.TooBig
? "A too big message has been received."
: code == CloseStatusCode.MissingExtension
? "WebSocket client didn't receive expected extension(s)."
: code == CloseStatusCode.ServerError
? "WebSocket server got an internal error."
: code == CloseStatusCode.TlsHandshakeFailure
? "An error has occurred during a TLS handshake."
: string.Empty;
}
internal static string GetName(this string nameAndValue, char separator)
{
var idx = nameAndValue.IndexOf(separator);
return idx > 0 ? nameAndValue.Substring(0, idx).Trim() : null;
}
internal static string GetValue(this string nameAndValue, char separator)
{
var idx = nameAndValue.IndexOf(separator);
return idx > -1 && idx < nameAndValue.Length - 1
? nameAndValue.Substring(idx + 1).Trim()
: null;
}
internal static string GetValue(this string nameAndValue, char separator, bool unquote)
{
var idx = nameAndValue.IndexOf(separator);
if (idx < 0 || idx == nameAndValue.Length - 1)
{
return null;
}
var val = nameAndValue.Substring(idx + 1).Trim();
return unquote ? val.Unquote() : val;
}
internal static byte[] InternalToByteArray(this ushort value, ByteOrder order)
{
var bytes = BitConverter.GetBytes(value);
if (!order.IsHostOrder())
{
Array.Reverse(bytes);
}
return bytes;
}
internal static byte[] InternalToByteArray(this ulong value, ByteOrder order)
{
var bytes = BitConverter.GetBytes(value);
if (!order.IsHostOrder())
{
Array.Reverse(bytes);
}
return bytes;
}
internal static bool IsCompressionExtension(this string value, CompressionMethod method)
{
return value.StartsWith(method.ToExtensionString());
}
internal static bool IsControl(this byte opcode)
{
return opcode > 0x7 && opcode < 0x10;
}
internal static bool IsControl(this OperationCode opcode)
{
return opcode >= OperationCode.Close;
}
internal static bool IsData(this byte opcode)
{
return opcode == 0x1 || opcode == 0x2;
}
internal static bool IsData(this OperationCode opcode)
{
return opcode == OperationCode.Text || opcode == OperationCode.Binary;
}
internal static bool IsPortNumber(this int value)
{
return value > 0 && value < 65536;
}
internal static bool IsReserved(this ushort code)
{
return code == (ushort)CloseStatusCode.Undefined
|| code == (ushort)CloseStatusCode.NoStatus
|| code == (ushort)CloseStatusCode.Abnormal
|| code == (ushort)CloseStatusCode.TlsHandshakeFailure;
}
internal static bool IsReserved(this CloseStatusCode code)
{
return code == CloseStatusCode.Undefined
|| code == CloseStatusCode.NoStatus
|| code == CloseStatusCode.Abnormal
|| code == CloseStatusCode.TlsHandshakeFailure;
}
internal static bool IsSupported(this byte opcode)
{
return Enum.IsDefined(typeof(OperationCode), opcode);
}
internal static bool IsText(this string value)
{
var len = value.Length;
for (var i = 0; i < len; i++)
{
var c = value[i];
if (c < 0x20)
{
if (!"\r\n\t".Contains(c))
{
return false;
}
if (c == '\n')
{
i++;
if (i == len)
{
break;
}
c = value[i];
if (!" \t".Contains(c))
{
return false;
}
}
continue;
}
if (c == 0x7f)
{
return false;
}
}
return true;
}
internal static bool IsToken(this string value)
{
foreach (var c in value)
{
if (c < 0x20)
{
return false;
}
if (c >= 0x7f)
{
return false;
}
if (_tspecials.Contains(c))
{
return false;
}
}
return true;
}
internal static string Quote(this string value)
{
return string.Format("\"{0}\"", value.Replace("\"", "\\\""));
}
internal static byte[] ReadBytes(this Stream stream, int length)
{
var buff = new byte[length];
var offset = 0;
try
{
var nread = 0;
while (length > 0)
{
nread = stream.Read(buff, offset, length);
if (nread == 0)
{
break;
}
offset += nread;
length -= nread;
}
}
catch
{
}
return buff.SubArray(0, offset);
}
internal static byte[] ReadBytes(this Stream stream, long length, int bufferLength)
{
using (var dest = new MemoryStream())
{
try
{
var buff = new byte[bufferLength];
var nread = 0;
while (length > 0)
{
if (length < bufferLength)
{
bufferLength = (int)length;
}
nread = stream.Read(buff, 0, bufferLength);
if (nread == 0)
{
break;
}
dest.Write(buff, 0, nread);
length -= nread;
}
}
catch
{
}
dest.Close();
return dest.ToArray();
}
}
internal static void ReadBytesAsync(
this Stream stream, int length, Action<byte[]> completed, Action<Exception> error
)
{
var buff = new byte[length];
var offset = 0;
var retry = 0;
AsyncCallback callback = null;
callback =
ar =>
{
try
{
var nread = stream.EndRead(ar);
if (nread == 0 && retry < _retry)
{
retry++;
stream.BeginRead(buff, offset, length, callback, null);
return;
}
if (nread == length)
{
completed?.Invoke(buff.SubArray(0, offset + nread));
return;
}
retry = 0;
offset += nread;
length -= nread;
stream.BeginRead(buff, offset, length, callback, null);
}
catch (Exception ex)
{
error?.Invoke(ex);
}
};
try
{
stream.BeginRead(buff, offset, length, callback, null);
}
catch (Exception ex)
{
error?.Invoke(ex);
}
}
internal static void ReadBytesAsync(
this Stream stream,
long length,
int bufferLength,
Action<byte[]> completed,
Action<Exception> error
)
{
var dest = new MemoryStream();
var buff = new byte[bufferLength];
var retry = 0;
Action<long> read = null;
read =
len =>
{
if (len < bufferLength)
{
bufferLength = (int)len;
}
stream.BeginRead(
buff,
0,
bufferLength,
ar =>
{
try
{
var nread = stream.EndRead(ar);
if (nread > 0)
{
dest.Write(buff, 0, nread);
}
if (nread == 0 && retry < _retry)
{
retry++;
read(len);
return;
}
if (nread == 0 || nread == len)
{
if (completed != null)
{
dest.Close();
completed(dest.ToArray());
}
dest.Dispose();
return;
}
retry = 0;
read(len - nread);
}
catch (Exception ex)
{
dest.Dispose();
error?.Invoke(ex);
}
},
null
);
};
try
{
read(length);
}
catch (Exception ex)
{
dest.Dispose();
error?.Invoke(ex);
}
}
internal static string RemovePrefix(this string value, params string[] prefixes)
{
var idx = 0;
foreach (var prefix in prefixes)
{
if (value.StartsWith(prefix))
{
idx = prefix.Length;
break;
}
}
return idx > 0 ? value.Substring(idx) : value;
}
internal static T[] Reverse<T>(this T[] array)
{
var len = array.Length;
var reverse = new T[len];
var end = len - 1;
for (var i = 0; i <= end; i++)
{
reverse[i] = array[end - i];
}
return reverse;
}
internal static IEnumerable<string> SplitHeaderValue(
this string value, params char[] separators)
{
var len = value.Length;
var seps = new string(separators);
var buff = new StringBuilder(32);
var escaped = false;
var quoted = false;
for (var i = 0; i < len; i++)
{
var c = value[i];
if (c == '"')
{
if (escaped)
{
escaped = !escaped;
}
else
{
quoted = !quoted;
}
}
else if (c == '\\')
{
if (i < len - 1 && value[i + 1] == '"')
{
escaped = true;
}
}
else if (seps.Contains(c))
{
if (!quoted)
{
yield return buff.ToString();
buff.Length = 0;
continue;
}
}
else
{
}
buff.Append(c);
}
if (buff.Length > 0)
{
yield return buff.ToString();
}
}
internal static byte[] ToByteArray(this Stream stream)
{
using (var output = new MemoryStream())
{
stream.Position = 0;
stream.CopyTo(output, BUFFER_SIZE);
output.Close();
return output.ToArray();
}
}
internal static CompressionMethod ToCompressionMethod(this string value)
{
foreach (CompressionMethod method in Enum.GetValues(typeof(CompressionMethod)))
{
if (method.ToExtensionString() == value)
{
return method;
}
}
return CompressionMethod.None;
}
internal static string ToExtensionString(
this CompressionMethod method, params string[] parameters)
{
if (method == CompressionMethod.None)
{
return string.Empty;
}
var m = string.Format("permessage-{0}", method.ToString().ToLower());
if (parameters == null || parameters.Length == 0)
{
return m;
}
return string.Format("{0}; {1}", m, parameters.ToString("; "));
}
internal static System.Net.IPAddress ToIPAddress(this string value)
{
if (value == null || value.Length == 0)
{
return null;
}
if (System.Net.IPAddress.TryParse(value, out System.Net.IPAddress addr))
{
return addr;
}
try
{
var addrs = System.Net.Dns.GetHostAddresses(value);
return addrs[0];
}
catch
{
return null;
}
}
internal static List<TSource> ToList<TSource>(this IEnumerable<TSource> source)
{
return new List<TSource>(source);
}
internal static string ToString(
this System.Net.IPAddress address, bool bracketIPv6
)
{
return bracketIPv6 && address.AddressFamily == AddressFamily.InterNetworkV6
? string.Format("[{0}]", address.ToString())
: address.ToString();
}
internal static ushort ToUInt16(this byte[] source, ByteOrder sourceOrder)
{
return BitConverter.ToUInt16(source.ToHostOrder(sourceOrder), 0);
}
internal static ulong ToUInt64(this byte[] source, ByteOrder sourceOrder)
{
return BitConverter.ToUInt64(source.ToHostOrder(sourceOrder), 0);
}
internal static string TrimSlashFromEnd(this string value)
{
var ret = value.TrimEnd('/');
return ret.Length > 0 ? ret : "/";
}
internal static string TrimSlashOrBackslashFromEnd(this string value)
{
var ret = value.TrimEnd('/', '\\');
return ret.Length > 0 ? ret : value[0].ToString();
}
internal static bool TryCreateWebSocketUri(
this string uriString, out Uri result, out string message
)
{
result = null;
message = null;
var uri = uriString.ToUri();
if (uri == null)
{
message = "An invalid URI string.";
return false;
}
if (!uri.IsAbsoluteUri)
{
message = "A relative URI.";
return false;
}
var scheme = uri.Scheme;
if (!(scheme == "ws" || scheme == "wss"))
{
message = "The scheme part is not 'ws' or 'wss'.";
return false;
}
var port = uri.Port;
if (port == 0)
{
message = "The port part is zero.";
return false;
}
if (uri.Fragment.Length > 0)
{
message = "It includes the fragment component.";
return false;
}
result = port != -1
? uri
: new Uri(
$"{scheme}://{uri.Host}:{(scheme == "ws" ? 80 : 443)}{uri.PathAndQuery}"
);
return true;
}
internal static bool TryGetUTF8DecodedString(this byte[] bytes, out string s)
{
s = null;
try
{
s = Encoding.UTF8.GetString(bytes);
}
catch
{
return false;
}
return true;
}
internal static bool TryGetUTF8EncodedBytes(this string s, out byte[] bytes)
{
bytes = null;
try
{
bytes = Encoding.UTF8.GetBytes(s);
}
catch
{
return false;
}
return true;
}
internal static bool TryOpenRead(
this FileInfo fileInfo, out FileStream fileStream
)
{
fileStream = null;
try
{
fileStream = fileInfo.OpenRead();
}
catch
{
return false;
}
return true;
}
internal static string Unquote(this string value)
{
var start = value.IndexOf('"');
if (start < 0)
{
return value;
}
var end = value.LastIndexOf('"');
var len = end - start - 1;
return len < 0
? value
: len == 0
? string.Empty
: value.Substring(start + 1, len).Replace("\\\"", "\"");
}
internal static string UTF8Decode(this byte[] bytes)
{
try
{
return Encoding.UTF8.GetString(bytes);
}
catch
{
return null;
}
}
internal static byte[] UTF8Encode(this string s)
{
return Encoding.UTF8.GetBytes(s);
}
internal static void WriteBytes(this Stream stream, byte[] bytes, int bufferLength)
{
using (var input = new MemoryStream(bytes))
{
input.CopyTo(stream, bufferLength);
}
}
internal static void WriteBytesAsync(
this Stream stream, byte[] bytes, int bufferLength, Action completed, Action<Exception> error)
{
var input = new MemoryStream(bytes);
input.CopyToAsync(
stream,
bufferLength,
() =>
{
completed?.Invoke();
input.Dispose();
},
ex =>
{
input.Dispose();
error?.Invoke(ex);
});
}
private static byte[] compress(this byte[] data)
{
if (data.LongLength == 0)
{
return data;
}
using (var input = new MemoryStream(data))
{
return input.compressToArray();
}
}
private static MemoryStream compress(this Stream stream)
{
var output = new MemoryStream();
if (stream.Length == 0)
{
return output;
}
stream.Position = 0;
using (var ds = new DeflateStream(output, CompressionMode.Compress, true))
{
stream.CopyTo(ds, BUFFER_SIZE);
ds.Close(); // BFINAL set to 1.
output.Write(_last, 0, 1);
output.Position = 0;
return output;
}
}
private static byte[] compressToArray(this Stream stream)
{
using (var output = stream.compress())
{
output.Close();
return output.ToArray();
}
}
private static byte[] decompress(this byte[] data)
{
if (data.LongLength == 0)
{
return data;
}
using (var input = new MemoryStream(data))
{
return input.decompressToArray();
}
}
private static MemoryStream decompress(this Stream stream)
{
var output = new MemoryStream();
if (stream.Length == 0)
{
return output;
}
stream.Position = 0;
using (var ds = new DeflateStream(stream, CompressionMode.Decompress, true))
{
ds.CopyTo(output, BUFFER_SIZE);
output.Position = 0;
return output;
}
}
private static byte[] decompressToArray(this Stream stream)
{
using (var output = stream.decompress())
{
output.Close();
return output.ToArray();
}
}
private static void times(this ulong n, Action action)
{
for (ulong i = 0; i < n; i++)
{
action();
}
}
}
}