12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868 |
- #region License
- /*
- * Ext.cs
- *
- * Some parts of this code are derived from Mono (http://www.mono-project.com):
- * - GetStatusDescription is derived from HttpListenerResponse.cs (System.Net)
- * - MaybeUri is derived from Uri.cs (System)
- * - isPredefinedScheme is derived from Uri.cs (System)
- *
- * The MIT License
- *
- * Copyright (c) 2001 Garrett Rooney
- * Copyright (c) 2003 Ian MacLean
- * Copyright (c) 2003 Ben Maurer
- * Copyright (c) 2003, 2005, 2009 Novell, Inc. (http://www.novell.com)
- * Copyright (c) 2009 Stephane Delcroix
- * Copyright (c) 2010-2023 sta.blockhead
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
- #endregion
- #region Contributors
- /*
- * Contributors:
- * - Liryna <liryna.stark@gmail.com>
- * - Nikola Kovacevic <nikolak@outlook.com>
- * - Chris Swiedler
- */
- #endregion
- using System;
- using System.Collections.Generic;
- using System.Collections.Specialized;
- using System.IO;
- using System.IO.Compression;
- using System.Net.Sockets;
- using System.Security.Cryptography.X509Certificates;
- using System.Text;
- using WebSocketSharp.Net;
- using WebSocketSharp.Net.WebSockets;
- using WebSocketSharp.Server;
- namespace WebSocketSharp
- {
- /// <summary>
- /// Provides a set of static methods for websocket-sharp.
- /// </summary>
- public static class Ext
- {
- #region Private Fields
- private static readonly byte[] _last = new byte[] { 0x00 };
- private static readonly int _maxRetry = 5;
- private const string _tspecials = "()<>@,;:\\\"/[]?={} \t";
- #endregion
- #region Private Methods
- 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 ret = new MemoryStream ();
- if (stream.Length == 0)
- return ret;
- stream.Position = 0;
- var mode = CompressionMode.Compress;
- using (var ds = new DeflateStream (ret, mode, true)) {
- stream.CopyTo (ds, 1024);
- ds.Close (); // BFINAL set to 1.
- ret.Write (_last, 0, 1);
- ret.Position = 0;
- return ret;
- }
- }
- 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 ret = new MemoryStream ();
- if (stream.Length == 0)
- return ret;
- stream.Position = 0;
- var mode = CompressionMode.Decompress;
- using (var ds = new DeflateStream (stream, mode, true)) {
- ds.CopyTo (ret, 1024);
- ret.Position = 0;
- return ret;
- }
- }
- private static byte[] decompressToArray (this Stream stream)
- {
- using (var output = stream.decompress ()) {
- output.Close ();
- return output.ToArray ();
- }
- }
- private static bool isPredefinedScheme (this string value)
- {
- 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;
- }
- #endregion
- #region Internal Methods
- internal static byte[] Append (this ushort code, string reason)
- {
- var codeAsBytes = code.ToByteArray (ByteOrder.Big);
- if (reason == null || reason.Length == 0)
- return codeAsBytes;
- var buff = new List<byte> (codeAsBytes);
- var reasonAsBytes = Encoding.UTF8.GetBytes (reason);
- buff.AddRange (reasonAsBytes);
- return buff.ToArray ();
- }
- 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 bool Contains (this string value, params char[] anyOf)
- {
- return anyOf != null && anyOf.Length > 0
- ? value.IndexOfAny (anyOf) > -1
- : false;
- }
- internal static bool Contains (
- this NameValueCollection collection, string name
- )
- {
- return collection[name] != null;
- }
- internal static bool Contains (
- this NameValueCollection collection,
- string name,
- string value,
- StringComparison comparisonTypeForValue
- )
- {
- var val = collection[name];
- if (val == null)
- return false;
- foreach (var elm in val.Split (',')) {
- if (elm.Trim ().Equals (value, comparisonTypeForValue))
- return true;
- }
- return false;
- }
- 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[] sourceArray, int length)
- {
- var dest = new T[length];
- Array.Copy (sourceArray, 0, dest, 0, length);
- return dest;
- }
- internal static T[] Copy<T> (this T[] sourceArray, long length)
- {
- var dest = new T[length];
- Array.Copy (sourceArray, 0, dest, 0, length);
- return dest;
- }
- internal static void CopyTo (
- this Stream sourceStream, Stream destinationStream, int bufferLength
- )
- {
- var buff = new byte[bufferLength];
- while (true) {
- var nread = sourceStream.Read (buff, 0, bufferLength);
- if (nread <= 0)
- break;
- destinationStream.Write (buff, 0, nread);
- }
- }
- internal static void CopyToAsync (
- this Stream sourceStream,
- Stream destinationStream,
- int bufferLength,
- Action completed,
- Action<Exception> error
- )
- {
- var buff = new byte[bufferLength];
- AsyncCallback callback = null;
- callback =
- ar => {
- try {
- var nread = sourceStream.EndRead (ar);
- if (nread <= 0) {
- if (completed != null)
- completed ();
- return;
- }
- destinationStream.Write (buff, 0, nread);
- sourceStream.BeginRead (buff, 0, bufferLength, callback, null);
- }
- catch (Exception ex) {
- if (error != null)
- error (ex);
- }
- };
- try {
- sourceStream.BeginRead (buff, 0, bufferLength, callback, null);
- }
- catch (Exception ex) {
- if (error != null)
- error (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 void Emit (
- this EventHandler eventHandler, object sender, EventArgs e
- )
- {
- if (eventHandler == null)
- return;
- eventHandler (sender, e);
- }
- internal static void Emit<TEventArgs> (
- this EventHandler<TEventArgs> eventHandler, object sender, TEventArgs e
- )
- where TEventArgs : EventArgs
- {
- if (eventHandler == null)
- return;
- eventHandler (sender, e);
- }
- 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 CookieCollection GetCookies (
- this NameValueCollection headers, bool response
- )
- {
- var val = headers[response ? "Set-Cookie" : "Cookie"];
- return val != null
- ? CookieCollection.Parse (val, response)
- : new CookieCollection ();
- }
- internal static string GetDnsSafeHost (this Uri uri, bool bracketIPv6)
- {
- return bracketIPv6 && uri.HostNameType == UriHostNameType.IPv6
- ? uri.Host
- : uri.DnsSafeHost;
- }
- internal static string GetErrorMessage (this ushort code)
- {
- switch (code) {
- case 1002:
- return "A protocol error has occurred.";
- case 1003:
- return "Unsupported data has been received.";
- case 1006:
- return "An abnormal error has occurred.";
- case 1007:
- return "Invalid data has been received.";
- case 1008:
- return "A policy violation has occurred.";
- case 1009:
- return "A too big message has been received.";
- case 1010:
- return "The client did not receive expected extension(s).";
- case 1011:
- return "The server got an internal error.";
- case 1015:
- return "An error has occurred during a TLS handshake.";
- default:
- return String.Empty;
- }
- }
- internal static string GetErrorMessage (this CloseStatusCode code)
- {
- return ((ushort) code).GetErrorMessage ();
- }
- 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 GetUTF8DecodedString (this byte[] bytes)
- {
- try {
- return Encoding.UTF8.GetString (bytes);
- }
- catch {
- return null;
- }
- }
- internal static byte[] GetUTF8EncodedBytes (this string s)
- {
- try {
- return Encoding.UTF8.GetBytes (s);
- }
- catch {
- return null;
- }
- }
- internal static string GetValue (this string nameAndValue, char separator)
- {
- return nameAndValue.GetValue (separator, false);
- }
- 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 bool IsCompressionExtension (
- this string value, CompressionMethod method
- )
- {
- var extStr = method.ToExtensionString ();
- var compType = StringComparison.Ordinal;
- return value.StartsWith (extStr, compType);
- }
- internal static bool IsEqualTo (
- this int value, char c, Action<int> beforeComparing
- )
- {
- beforeComparing (value);
- return value == c - 0;
- }
- internal static bool IsHttpMethod (this string value)
- {
- return value == "GET"
- || value == "HEAD"
- || value == "POST"
- || value == "PUT"
- || value == "DELETE"
- || value == "CONNECT"
- || value == "OPTIONS"
- || value == "TRACE";
- }
- internal static bool IsPortNumber (this int value)
- {
- return value > 0 && value < 65536;
- }
- internal static bool IsReserved (this CloseStatusCode code)
- {
- return ((ushort) code).IsReservedStatusCode ();
- }
- internal static bool IsReservedStatusCode (this ushort code)
- {
- return code == 1004
- || code == 1005
- || code == 1006
- || code == 1015;
- }
- internal static bool IsSupportedOpcode (this byte opcode)
- {
- return Enum.IsDefined (typeof (Opcode), 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".IndexOf (c) == -1)
- return false;
- if (c == '\n') {
- i++;
- if (i == len)
- break;
- c = value[i];
- if (" \t".IndexOf (c) == -1)
- 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 > 0x7e)
- return false;
- if (_tspecials.IndexOf (c) > -1)
- return false;
- }
- return true;
- }
- internal static bool KeepsAlive (
- this NameValueCollection headers, Version version
- )
- {
- var compType = StringComparison.OrdinalIgnoreCase;
- return version < HttpVersion.Version11
- ? headers.Contains ("Connection", "keep-alive", compType)
- : !headers.Contains ("Connection", "close", compType);
- }
- internal static bool MaybeUri (this string value)
- {
- var idx = value.IndexOf (':');
- if (idx < 2 || idx > 9)
- return false;
- var schm = value.Substring (0, idx);
- return schm.isPredefinedScheme ();
- }
- internal static string Quote (this string value)
- {
- var fmt = "\"{0}\"";
- var val = value.Replace ("\"", "\\\"");
- return String.Format (fmt, val);
- }
- internal static byte[] ReadBytes (this Stream stream, int length)
- {
- var ret = new byte[length];
- var offset = 0;
- var retry = 0;
- while (length > 0) {
- var nread = stream.Read (ret, offset, length);
- if (nread <= 0) {
- if (retry < _maxRetry) {
- retry++;
- continue;
- }
- return ret.SubArray (0, offset);
- }
- retry = 0;
- offset += nread;
- length -= nread;
- }
- return ret;
- }
- internal static byte[] ReadBytes (
- this Stream stream, long length, int bufferLength
- )
- {
- using (var dest = new MemoryStream ()) {
- var buff = new byte[bufferLength];
- var retry = 0;
- while (length > 0) {
- if (length < bufferLength)
- bufferLength = (int) length;
- var nread = stream.Read (buff, 0, bufferLength);
- if (nread <= 0) {
- if (retry < _maxRetry) {
- retry++;
- continue;
- }
- break;
- }
- retry = 0;
- dest.Write (buff, 0, nread);
- length -= nread;
- }
- dest.Close ();
- return dest.ToArray ();
- }
- }
- internal static void ReadBytesAsync (
- this Stream stream,
- int length,
- Action<byte[]> completed,
- Action<Exception> error
- )
- {
- var ret = new byte[length];
- var offset = 0;
- var retry = 0;
- AsyncCallback callback = null;
- callback =
- ar => {
- try {
- var nread = stream.EndRead (ar);
- if (nread <= 0) {
- if (retry < _maxRetry) {
- retry++;
- stream.BeginRead (ret, offset, length, callback, null);
- return;
- }
- if (completed != null)
- completed (ret.SubArray (0, offset));
- return;
- }
- if (nread == length) {
- if (completed != null)
- completed (ret);
- return;
- }
- retry = 0;
- offset += nread;
- length -= nread;
- stream.BeginRead (ret, offset, length, callback, null);
- }
- catch (Exception ex) {
- if (error != null)
- error (ex);
- }
- };
- try {
- stream.BeginRead (ret, offset, length, callback, null);
- }
- catch (Exception ex) {
- if (error != null)
- error (ex);
- }
- }
- internal static void ReadBytesAsync (
- this Stream stream,
- long length,
- int bufferLength,
- Action<byte[]> completed,
- Action<Exception> error
- )
- {
- Console.WriteLine(("Into ReadBytesAsync"));
- var dest = new MemoryStream ();
-
- var retry = 0;
- Action<long> read = null;
- read =
- len => {
- Console.WriteLine("Into Read()");
-
- if (len < bufferLength)
- bufferLength = (int) len;
- var buff = new byte[bufferLength];
- stream.BeginRead (
- buff,
- 0,
- bufferLength,
- ar => {
- try {
- var nread = stream.EndRead (ar);
- if (nread <= 0) {
- if (retry < _maxRetry) {
- retry++;
- read (len);
- return;
- }
- if (completed != null) {
- dest.Close ();
- var ret = dest.ToArray ();
- completed (ret);
- }
- dest.Dispose ();
- return;
- }
- dest.Write (buff, 0, nread);
- if (nread == len) {
- if (completed != null) {
- dest.Close ();
-
- var ret = dest.ToArray ();
- completed (ret);
- }
- dest.Dispose ();
- return;
- }
- retry = 0;
- read (len - nread);
- }
- catch (Exception ex) {
- dest.Dispose ();
- if (error != null)
- error (ex);
- }
- },
- null
- );
- };
- try {
- read (length);
- }
- catch (Exception ex) {
- dest.Dispose ();
- if (error != null)
- error (ex);
- }
- }
- internal static T[] Reverse<T> (this T[] array)
- {
- var len = array.LongLength;
- var ret = new T[len];
- var end = len - 1;
- for (long i = 0; i <= end; i++)
- ret[i] = array[end - i];
- return ret;
- }
- internal static IEnumerable<string> SplitHeaderValue (
- this string value, params char[] separators
- )
- {
- var len = value.Length;
- var end = len - 1;
- var buff = new StringBuilder (32);
- var escaped = false;
- var quoted = false;
- for (var i = 0; i <= end; i++) {
- var c = value[i];
- buff.Append (c);
- if (c == '"') {
- if (escaped) {
- escaped = false;
- continue;
- }
- quoted = !quoted;
- continue;
- }
- if (c == '\\') {
- if (i == end)
- break;
- if (value[i + 1] == '"')
- escaped = true;
- continue;
- }
- if (Array.IndexOf (separators, c) > -1) {
- if (quoted)
- continue;
- buff.Length -= 1;
- yield return buff.ToString ();
- buff.Length = 0;
- continue;
- }
- }
- yield return buff.ToString ();
- }
- internal static byte[] ToByteArray (this Stream stream)
- {
- stream.Position = 0;
- using (var buff = new MemoryStream ()) {
- stream.CopyTo (buff, 1024);
- buff.Close ();
- return buff.ToArray ();
- }
- }
- internal static byte[] ToByteArray (this ushort value, ByteOrder order)
- {
- var ret = BitConverter.GetBytes (value);
- if (!order.IsHostOrder ())
- Array.Reverse (ret);
- return ret;
- }
- internal static byte[] ToByteArray (this ulong value, ByteOrder order)
- {
- var ret = BitConverter.GetBytes (value);
- if (!order.IsHostOrder ())
- Array.Reverse (ret);
- return ret;
- }
- internal static CompressionMethod ToCompressionMethod (this string value)
- {
- var methods = Enum.GetValues (typeof (CompressionMethod));
- foreach (CompressionMethod method in methods) {
- 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 name = method.ToString ().ToLower ();
- var ename = String.Format ("permessage-{0}", name);
- if (parameters == null || parameters.Length == 0)
- return ename;
- var eparams = parameters.ToString ("; ");
- return String.Format ("{0}; {1}", ename, eparams);
- }
- internal static int ToInt32 (this string numericString)
- {
- return Int32.Parse (numericString);
- }
- internal static System.Net.IPAddress ToIPAddress (this string value)
- {
- if (value == null || value.Length == 0)
- return null;
- System.Net.IPAddress addr;
- if (System.Net.IPAddress.TryParse (value, out 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)
- : address.ToString ();
- }
- internal static ushort ToUInt16 (this byte[] source, ByteOrder sourceOrder)
- {
- var val = source.ToHostOrder (sourceOrder);
- return BitConverter.ToUInt16 (val, 0);
- }
- internal static ulong ToUInt64 (this byte[] source, ByteOrder sourceOrder)
- {
- var val = source.ToHostOrder (sourceOrder);
- return BitConverter.ToUInt64 (val, 0);
- }
- internal static Version ToVersion (this string versionString)
- {
- return new Version (versionString);
- }
- internal static IEnumerable<string> TrimEach (
- this IEnumerable<string> source
- )
- {
- foreach (var elm in source)
- yield return elm.Trim ();
- }
- 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 TryCreateVersion (
- this string versionString, out Version result
- )
- {
- result = null;
- try {
- result = new Version (versionString);
- }
- catch {
- return false;
- }
- return true;
- }
- 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 schm = uri.Scheme;
- var valid = schm == "ws" || schm == "wss";
- if (!valid) {
- 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;
- }
- if (port == -1) {
- port = schm == "ws" ? 80 : 443;
- uriString = String.Format (
- "{0}://{1}:{2}{3}",
- schm,
- uri.Host,
- port,
- uri.PathAndQuery
- );
- result = new Uri (uriString);
- }
- else {
- result = uri;
- }
- 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 first = value.IndexOf ('"');
- if (first == -1)
- return value;
- var last = value.LastIndexOf ('"');
- if (last == first)
- return value;
- var len = last - first - 1;
- return len > 0
- ? value.Substring (first + 1, len).Replace ("\\\"", "\"")
- : String.Empty;
- }
- internal static bool Upgrades (
- this NameValueCollection headers, string protocol
- )
- {
- var compType = StringComparison.OrdinalIgnoreCase;
- return headers.Contains ("Upgrade", protocol, compType)
- && headers.Contains ("Connection", "Upgrade", compType);
- }
- internal static string UrlDecode (this string value, Encoding encoding)
- {
- return value.IndexOfAny (new[] { '%', '+' }) > -1
- ? HttpUtility.UrlDecode (value, encoding)
- : value;
- }
- internal static string UrlEncode (this string value, Encoding encoding)
- {
- return HttpUtility.UrlEncode (value, encoding);
- }
- internal static void WriteBytes (
- this Stream stream, byte[] bytes, int bufferLength
- )
- {
- using (var src = new MemoryStream (bytes))
- src.CopyTo (stream, bufferLength);
- }
- internal static void WriteBytesAsync (
- this Stream stream,
- byte[] bytes,
- int bufferLength,
- Action completed,
- Action<Exception> error
- )
- {
- var src = new MemoryStream (bytes);
- src.CopyToAsync (
- stream,
- bufferLength,
- () => {
- if (completed != null)
- completed ();
- src.Dispose ();
- },
- ex => {
- src.Dispose ();
- if (error != null)
- error (ex);
- }
- );
- }
- #endregion
- #region Public Methods
- /// <summary>
- /// Gets the description of the specified HTTP status code.
- /// </summary>
- /// <returns>
- /// A <see cref="string"/> that represents the description of
- /// the HTTP status code.
- /// </returns>
- /// <param name="code">
- /// <para>
- /// One of the <see cref="HttpStatusCode"/> enum values.
- /// </para>
- /// <para>
- /// It specifies the HTTP status code.
- /// </para>
- /// </param>
- public static string GetDescription (this HttpStatusCode code)
- {
- return ((int) code).GetStatusDescription ();
- }
- /// <summary>
- /// Gets the description of the specified HTTP status code.
- /// </summary>
- /// <returns>
- /// <para>
- /// A <see cref="string"/> that represents the description of
- /// the HTTP status code.
- /// </para>
- /// <para>
- /// An empty string if the description is not present.
- /// </para>
- /// </returns>
- /// <param name="code">
- /// An <see cref="int"/> that specifies the HTTP status code.
- /// </param>
- 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;
- }
- /// <summary>
- /// Determines whether the specified ushort is in the range of
- /// the status code for the WebSocket connection close.
- /// </summary>
- /// <remarks>
- /// <para>
- /// The ranges are the following:
- /// </para>
- /// <list type="bullet">
- /// <item>
- /// <term>
- /// 1000-2999: These numbers are reserved for definition by
- /// the WebSocket protocol.
- /// </term>
- /// </item>
- /// <item>
- /// <term>
- /// 3000-3999: These numbers are reserved for use by libraries,
- /// frameworks, and applications.
- /// </term>
- /// </item>
- /// <item>
- /// <term>
- /// 4000-4999: These numbers are reserved for private use.
- /// </term>
- /// </item>
- /// </list>
- /// </remarks>
- /// <returns>
- /// <c>true</c> if <paramref name="value"/> is in the range of
- /// the status code for the close; otherwise, <c>false</c>.
- /// </returns>
- /// <param name="value">
- /// A <see cref="ushort"/> to test.
- /// </param>
- public static bool IsCloseStatusCode (this ushort value)
- {
- return value > 999 && value < 5000;
- }
- /// <summary>
- /// Determines whether the specified string is enclosed in
- /// the specified character.
- /// </summary>
- /// <returns>
- /// <c>true</c> if <paramref name="value"/> is enclosed in
- /// <paramref name="c"/>; otherwise, <c>false</c>.
- /// </returns>
- /// <param name="value">
- /// A <see cref="string"/> to test.
- /// </param>
- /// <param name="c">
- /// A <see cref="char"/> to find.
- /// </param>
- public static bool IsEnclosedIn (this string value, char c)
- {
- if (value == null)
- return false;
- var len = value.Length;
- return len > 1 ? value[0] == c && value[len - 1] == c : false;
- }
- /// <summary>
- /// Determines whether the specified byte order is host (this computer
- /// architecture) byte order.
- /// </summary>
- /// <returns>
- /// <c>true</c> if <paramref name="order"/> is host byte order; otherwise,
- /// <c>false</c>.
- /// </returns>
- /// <param name="order">
- /// One of the <see cref="ByteOrder"/> enum values to test.
- /// </param>
- 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>
- /// Determines whether the specified IP address is a local IP address.
- /// </summary>
- /// <remarks>
- /// This local means NOT REMOTE for the current host.
- /// </remarks>
- /// <returns>
- /// <c>true</c> if <paramref name="address"/> is a local IP address;
- /// otherwise, <c>false</c>.
- /// </returns>
- /// <param name="address">
- /// A <see cref="System.Net.IPAddress"/> to test.
- /// </param>
- /// <exception cref="ArgumentNullException">
- /// <paramref name="address"/> is <see langword="null"/>.
- /// </exception>
- public static bool IsLocal (this System.Net.IPAddress address)
- {
- if (address == null)
- throw new ArgumentNullException ("address");
- 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;
- }
- var name = System.Net.Dns.GetHostName ();
- var addrs = System.Net.Dns.GetHostAddresses (name);
- foreach (var addr in addrs) {
- if (address.Equals (addr))
- return true;
- }
- return false;
- }
- /// <summary>
- /// Determines whether the specified string is <see langword="null"/> or
- /// an empty string.
- /// </summary>
- /// <returns>
- /// <c>true</c> if <paramref name="value"/> is <see langword="null"/> or
- /// an empty string; otherwise, <c>false</c>.
- /// </returns>
- /// <param name="value">
- /// A <see cref="string"/> to test.
- /// </param>
- public static bool IsNullOrEmpty (this string value)
- {
- return value == null || value.Length == 0;
- }
- /// <summary>
- /// Retrieves a sub-array from the specified array. A sub-array starts at
- /// the specified index in the array.
- /// </summary>
- /// <returns>
- /// An array of T that receives a sub-array.
- /// </returns>
- /// <param name="array">
- /// An array of T from which to retrieve a sub-array.
- /// </param>
- /// <param name="startIndex">
- /// An <see cref="int"/> that specifies the zero-based index in the array
- /// at which retrieving starts.
- /// </param>
- /// <param name="length">
- /// An <see cref="int"/> that specifies the number of elements to retrieve.
- /// </param>
- /// <typeparam name="T">
- /// The type of elements in the array.
- /// </typeparam>
- /// <exception cref="ArgumentNullException">
- /// <paramref name="array"/> is <see langword="null"/>.
- /// </exception>
- /// <exception cref="ArgumentOutOfRangeException">
- /// <para>
- /// <paramref name="startIndex"/> is less than zero.
- /// </para>
- /// <para>
- /// -or-
- /// </para>
- /// <para>
- /// <paramref name="startIndex"/> is greater than the end of the array.
- /// </para>
- /// <para>
- /// -or-
- /// </para>
- /// <para>
- /// <paramref name="length"/> is less than zero.
- /// </para>
- /// <para>
- /// -or-
- /// </para>
- /// <para>
- /// <paramref name="length"/> is greater than the number of elements from
- /// <paramref name="startIndex"/> to the end of the array.
- /// </para>
- /// </exception>
- public static T[] SubArray<T> (this T[] array, int startIndex, int length)
- {
- if (array == null)
- throw new ArgumentNullException ("array");
- var len = array.Length;
- if (len == 0) {
- if (startIndex != 0)
- throw new ArgumentOutOfRangeException ("startIndex");
- if (length != 0)
- throw new ArgumentOutOfRangeException ("length");
- return array;
- }
- if (startIndex < 0 || startIndex >= len)
- throw new ArgumentOutOfRangeException ("startIndex");
- if (length < 0 || length > len - startIndex)
- throw new ArgumentOutOfRangeException ("length");
- if (length == 0)
- return new T[0];
- if (length == len)
- return array;
- var ret = new T[length];
- Array.Copy (array, startIndex, ret, 0, length);
- return ret;
- }
- /// <summary>
- /// Retrieves a sub-array from the specified array. A sub-array starts at
- /// the specified index in the array.
- /// </summary>
- /// <returns>
- /// An array of T that receives a sub-array.
- /// </returns>
- /// <param name="array">
- /// An array of T from which to retrieve a sub-array.
- /// </param>
- /// <param name="startIndex">
- /// A <see cref="long"/> that specifies the zero-based index in the array
- /// at which retrieving starts.
- /// </param>
- /// <param name="length">
- /// A <see cref="long"/> that specifies the number of elements to retrieve.
- /// </param>
- /// <typeparam name="T">
- /// The type of elements in the array.
- /// </typeparam>
- /// <exception cref="ArgumentNullException">
- /// <paramref name="array"/> is <see langword="null"/>.
- /// </exception>
- /// <exception cref="ArgumentOutOfRangeException">
- /// <para>
- /// <paramref name="startIndex"/> is less than zero.
- /// </para>
- /// <para>
- /// -or-
- /// </para>
- /// <para>
- /// <paramref name="startIndex"/> is greater than the end of the array.
- /// </para>
- /// <para>
- /// -or-
- /// </para>
- /// <para>
- /// <paramref name="length"/> is less than zero.
- /// </para>
- /// <para>
- /// -or-
- /// </para>
- /// <para>
- /// <paramref name="length"/> is greater than the number of elements from
- /// <paramref name="startIndex"/> to the end of the array.
- /// </para>
- /// </exception>
- public static T[] SubArray<T> (this T[] array, long startIndex, long length)
- {
- if (array == null)
- throw new ArgumentNullException ("array");
- var len = array.LongLength;
- if (len == 0) {
- if (startIndex != 0)
- throw new ArgumentOutOfRangeException ("startIndex");
- if (length != 0)
- throw new ArgumentOutOfRangeException ("length");
- return array;
- }
- if (startIndex < 0 || startIndex >= len)
- throw new ArgumentOutOfRangeException ("startIndex");
- if (length < 0 || length > len - startIndex)
- throw new ArgumentOutOfRangeException ("length");
- if (length == 0)
- return new T[0];
- if (length == len)
- return array;
- var ret = new T[length];
- Array.Copy (array, startIndex, ret, 0, length);
- return ret;
- }
- /// <summary>
- /// Executes the specified delegate <paramref name="n"/> times.
- /// </summary>
- /// <param name="n">
- /// An <see cref="int"/> that specifies the number of times to execute.
- /// </param>
- /// <param name="action">
- /// <para>
- /// An <c>Action<int></c> delegate to execute.
- /// </para>
- /// <para>
- /// The <see cref="int"/> parameter is the zero-based count of iteration.
- /// </para>
- /// </param>
- public static void Times (this int n, Action<int> action)
- {
- if (n <= 0)
- return;
- if (action == null)
- return;
- for (int i = 0; i < n; i++)
- action (i);
- }
- /// <summary>
- /// Executes the specified delegate <paramref name="n"/> times.
- /// </summary>
- /// <param name="n">
- /// A <see cref="long"/> that specifies the number of times to execute.
- /// </param>
- /// <param name="action">
- /// <para>
- /// An <c>Action<long></c> delegate to execute.
- /// </para>
- /// <para>
- /// The <see cref="long"/> parameter is the zero-based count of iteration.
- /// </para>
- /// </param>
- public static void Times (this long n, Action<long> action)
- {
- if (n <= 0)
- return;
- if (action == null)
- return;
- for (long i = 0; i < n; i++)
- action (i);
- }
- /// <summary>
- /// Converts the order of elements in the specified byte array to
- /// host (this computer architecture) byte order.
- /// </summary>
- /// <returns>
- /// <para>
- /// An array of <see cref="byte"/> converted from
- /// <paramref name="source"/>.
- /// </para>
- /// <para>
- /// <paramref name="source"/> if the number of elements in
- /// it is less than 2 or <paramref name="sourceOrder"/> is
- /// same as host byte order.
- /// </para>
- /// </returns>
- /// <param name="source">
- /// An array of <see cref="byte"/> to convert.
- /// </param>
- /// <param name="sourceOrder">
- /// <para>
- /// One of the <see cref="ByteOrder"/> enum values.
- /// </para>
- /// <para>
- /// It specifies the order of elements in <paramref name="source"/>.
- /// </para>
- /// </param>
- /// <exception cref="ArgumentNullException">
- /// <paramref name="source"/> is <see langword="null"/>.
- /// </exception>
- public static byte[] ToHostOrder (this byte[] source, ByteOrder sourceOrder)
- {
- if (source == null)
- throw new ArgumentNullException ("source");
- if (source.Length < 2)
- return source;
- if (sourceOrder.IsHostOrder ())
- return source;
- return source.Reverse ();
- }
- /// <summary>
- /// Converts the specified array to a string.
- /// </summary>
- /// <returns>
- /// <para>
- /// A <see cref="string"/> converted by concatenating each element of
- /// <paramref name="array"/> across <paramref name="separator"/>.
- /// </para>
- /// <para>
- /// An empty string if <paramref name="array"/> is an empty array.
- /// </para>
- /// </returns>
- /// <param name="array">
- /// An array of T to convert.
- /// </param>
- /// <param name="separator">
- /// A <see cref="string"/> used to separate each element of
- /// <paramref name="array"/>.
- /// </param>
- /// <typeparam name="T">
- /// The type of elements in <paramref name="array"/>.
- /// </typeparam>
- /// <exception cref="ArgumentNullException">
- /// <paramref name="array"/> is <see langword="null"/>.
- /// </exception>
- public static string ToString<T> (this T[] array, string separator)
- {
- if (array == null)
- throw new ArgumentNullException ("array");
- var len = array.Length;
- if (len == 0)
- return String.Empty;
- var buff = new StringBuilder (64);
- var end = len - 1;
- for (var i = 0; i < end; i++)
- buff.AppendFormat ("{0}{1}", array[i], separator);
- buff.AppendFormat ("{0}", array[end]);
- return buff.ToString ();
- }
- /// <summary>
- /// Converts the specified string to a <see cref="Uri"/>.
- /// </summary>
- /// <returns>
- /// <para>
- /// A <see cref="Uri"/> converted from <paramref name="value"/>.
- /// </para>
- /// <para>
- /// <see langword="null"/> if the conversion has failed.
- /// </para>
- /// </returns>
- /// <param name="value">
- /// A <see cref="string"/> to convert.
- /// </param>
- public static Uri ToUri (this string value)
- {
- if (value == null || value.Length == 0)
- return null;
- var kind = value.MaybeUri () ? UriKind.Absolute : UriKind.Relative;
- Uri ret;
- Uri.TryCreate (value, kind, out ret);
- return ret;
- }
- #endregion
- }
- }
|