Ext.cs 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.Specialized;
  4. using System.IO;
  5. using System.IO.Compression;
  6. using System.Net;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. using MediaBrowser.Model.Services;
  10. using SocketHttpListener.Net;
  11. using HttpListenerResponse = SocketHttpListener.Net.HttpListenerResponse;
  12. using HttpStatusCode = SocketHttpListener.Net.HttpStatusCode;
  13. namespace SocketHttpListener
  14. {
  15. /// <summary>
  16. /// Provides a set of static methods for the websocket-sharp.
  17. /// </summary>
  18. public static class Ext
  19. {
  20. #region Private Const Fields
  21. private const string _tspecials = "()<>@,;:\\\"/[]?={} \t";
  22. #endregion
  23. #region Private Methods
  24. private static MemoryStream compress(this Stream stream)
  25. {
  26. var output = new MemoryStream();
  27. if (stream.Length == 0)
  28. return output;
  29. stream.Position = 0;
  30. using (var ds = new DeflateStream(output, CompressionMode.Compress, true))
  31. {
  32. stream.CopyTo(ds);
  33. //ds.Close(); // "BFINAL" set to 1.
  34. output.Position = 0;
  35. return output;
  36. }
  37. }
  38. private static byte[] decompress(this byte[] value)
  39. {
  40. if (value.Length == 0)
  41. return value;
  42. using (var input = new MemoryStream(value))
  43. {
  44. return input.decompressToArray();
  45. }
  46. }
  47. private static MemoryStream decompress(this Stream stream)
  48. {
  49. var output = new MemoryStream();
  50. if (stream.Length == 0)
  51. return output;
  52. stream.Position = 0;
  53. using (var ds = new DeflateStream(stream, CompressionMode.Decompress, true))
  54. {
  55. ds.CopyTo(output, true);
  56. return output;
  57. }
  58. }
  59. private static byte[] decompressToArray(this Stream stream)
  60. {
  61. using (var decomp = stream.decompress())
  62. {
  63. return decomp.ToArray();
  64. }
  65. }
  66. private static byte[] readBytes(this Stream stream, byte[] buffer, int offset, int length)
  67. {
  68. var len = stream.Read(buffer, offset, length);
  69. if (len < 1)
  70. return buffer.SubArray(0, offset);
  71. var tmp = 0;
  72. while (len < length)
  73. {
  74. tmp = stream.Read(buffer, offset + len, length - len);
  75. if (tmp < 1)
  76. break;
  77. len += tmp;
  78. }
  79. return len < length
  80. ? buffer.SubArray(0, offset + len)
  81. : buffer;
  82. }
  83. private static bool readBytes(
  84. this Stream stream, byte[] buffer, int offset, int length, Stream dest)
  85. {
  86. var bytes = stream.readBytes(buffer, offset, length);
  87. var len = bytes.Length;
  88. dest.Write(bytes, 0, len);
  89. return len == offset + length;
  90. }
  91. #endregion
  92. #region Internal Methods
  93. internal static byte[] Append(this ushort code, string reason)
  94. {
  95. using (var buffer = new MemoryStream())
  96. {
  97. var tmp = code.ToByteArrayInternally(ByteOrder.Big);
  98. buffer.Write(tmp, 0, 2);
  99. if (reason != null && reason.Length > 0)
  100. {
  101. tmp = Encoding.UTF8.GetBytes(reason);
  102. buffer.Write(tmp, 0, tmp.Length);
  103. }
  104. return buffer.ToArray();
  105. }
  106. }
  107. internal static string CheckIfClosable(this WebSocketState state)
  108. {
  109. return state == WebSocketState.Closing
  110. ? "While closing the WebSocket connection."
  111. : state == WebSocketState.Closed
  112. ? "The WebSocket connection has already been closed."
  113. : null;
  114. }
  115. internal static string CheckIfOpen(this WebSocketState state)
  116. {
  117. return state == WebSocketState.Connecting
  118. ? "A WebSocket connection isn't established."
  119. : state == WebSocketState.Closing
  120. ? "While closing the WebSocket connection."
  121. : state == WebSocketState.Closed
  122. ? "The WebSocket connection has already been closed."
  123. : null;
  124. }
  125. internal static string CheckIfValidControlData(this byte[] data, string paramName)
  126. {
  127. return data.Length > 125
  128. ? String.Format("'{0}' length must be less.", paramName)
  129. : null;
  130. }
  131. internal static string CheckIfValidSendData(this byte[] data)
  132. {
  133. return data == null
  134. ? "'data' must not be null."
  135. : null;
  136. }
  137. internal static string CheckIfValidSendData(this string data)
  138. {
  139. return data == null
  140. ? "'data' must not be null."
  141. : null;
  142. }
  143. internal static void Close(this HttpListenerResponse response, HttpStatusCode code)
  144. {
  145. response.StatusCode = (int)code;
  146. response.OutputStream.Dispose();
  147. }
  148. internal static Stream Compress(this Stream stream, CompressionMethod method)
  149. {
  150. return method == CompressionMethod.Deflate
  151. ? stream.compress()
  152. : stream;
  153. }
  154. internal static bool Contains<T>(this IEnumerable<T> source, Func<T, bool> condition)
  155. {
  156. foreach (T elm in source)
  157. if (condition(elm))
  158. return true;
  159. return false;
  160. }
  161. internal static void CopyTo(this Stream src, Stream dest, bool setDefaultPosition)
  162. {
  163. var readLen = 0;
  164. var bufferLen = 256;
  165. var buffer = new byte[bufferLen];
  166. while ((readLen = src.Read(buffer, 0, bufferLen)) > 0)
  167. {
  168. dest.Write(buffer, 0, readLen);
  169. }
  170. if (setDefaultPosition)
  171. dest.Position = 0;
  172. }
  173. internal static byte[] Decompress(this byte[] value, CompressionMethod method)
  174. {
  175. return method == CompressionMethod.Deflate
  176. ? value.decompress()
  177. : value;
  178. }
  179. internal static byte[] DecompressToArray(this Stream stream, CompressionMethod method)
  180. {
  181. return method == CompressionMethod.Deflate
  182. ? stream.decompressToArray()
  183. : stream.ToByteArray();
  184. }
  185. /// <summary>
  186. /// Determines whether the specified <see cref="int"/> equals the specified <see cref="char"/>,
  187. /// and invokes the specified Action&lt;int&gt; delegate at the same time.
  188. /// </summary>
  189. /// <returns>
  190. /// <c>true</c> if <paramref name="value"/> equals <paramref name="c"/>;
  191. /// otherwise, <c>false</c>.
  192. /// </returns>
  193. /// <param name="value">
  194. /// An <see cref="int"/> to compare.
  195. /// </param>
  196. /// <param name="c">
  197. /// A <see cref="char"/> to compare.
  198. /// </param>
  199. /// <param name="action">
  200. /// An Action&lt;int&gt; delegate that references the method(s) called at
  201. /// the same time as comparing. An <see cref="int"/> parameter to pass to
  202. /// the method(s) is <paramref name="value"/>.
  203. /// </param>
  204. /// <exception cref="ArgumentOutOfRangeException">
  205. /// <paramref name="value"/> isn't between 0 and 255.
  206. /// </exception>
  207. internal static bool EqualsWith(this int value, char c, Action<int> action)
  208. {
  209. if (value < 0 || value > 255)
  210. throw new ArgumentOutOfRangeException("value");
  211. action(value);
  212. return value == c - 0;
  213. }
  214. internal static string GetMessage(this CloseStatusCode code)
  215. {
  216. return code == CloseStatusCode.ProtocolError
  217. ? "A WebSocket protocol error has occurred."
  218. : code == CloseStatusCode.IncorrectData
  219. ? "An incorrect data has been received."
  220. : code == CloseStatusCode.Abnormal
  221. ? "An exception has occurred."
  222. : code == CloseStatusCode.InconsistentData
  223. ? "An inconsistent data has been received."
  224. : code == CloseStatusCode.PolicyViolation
  225. ? "A policy violation has occurred."
  226. : code == CloseStatusCode.TooBig
  227. ? "A too big data has been received."
  228. : code == CloseStatusCode.IgnoreExtension
  229. ? "WebSocket client did not receive expected extension(s)."
  230. : code == CloseStatusCode.ServerError
  231. ? "WebSocket server got an internal error."
  232. : code == CloseStatusCode.TlsHandshakeFailure
  233. ? "An error has occurred while handshaking."
  234. : String.Empty;
  235. }
  236. internal static string GetNameInternal(this string nameAndValue, string separator)
  237. {
  238. var i = nameAndValue.IndexOf(separator);
  239. return i > 0
  240. ? nameAndValue.Substring(0, i).Trim()
  241. : null;
  242. }
  243. internal static string GetValueInternal(this string nameAndValue, string separator)
  244. {
  245. var i = nameAndValue.IndexOf(separator);
  246. return i >= 0 && i < nameAndValue.Length - 1
  247. ? nameAndValue.Substring(i + 1).Trim()
  248. : null;
  249. }
  250. internal static bool IsCompressionExtension(this string value, CompressionMethod method)
  251. {
  252. return value.StartsWith(method.ToExtensionString());
  253. }
  254. internal static bool IsPortNumber(this int value)
  255. {
  256. return value > 0 && value < 65536;
  257. }
  258. internal static bool IsReserved(this ushort code)
  259. {
  260. return code == (ushort)CloseStatusCode.Undefined ||
  261. code == (ushort)CloseStatusCode.NoStatusCode ||
  262. code == (ushort)CloseStatusCode.Abnormal ||
  263. code == (ushort)CloseStatusCode.TlsHandshakeFailure;
  264. }
  265. internal static bool IsReserved(this CloseStatusCode code)
  266. {
  267. return code == CloseStatusCode.Undefined ||
  268. code == CloseStatusCode.NoStatusCode ||
  269. code == CloseStatusCode.Abnormal ||
  270. code == CloseStatusCode.TlsHandshakeFailure;
  271. }
  272. internal static bool IsText(this string value)
  273. {
  274. var len = value.Length;
  275. for (var i = 0; i < len; i++)
  276. {
  277. char c = value[i];
  278. if (c < 0x20 && !"\r\n\t".Contains(c))
  279. return false;
  280. if (c == 0x7f)
  281. return false;
  282. if (c == '\n' && ++i < len)
  283. {
  284. c = value[i];
  285. if (!" \t".Contains(c))
  286. return false;
  287. }
  288. }
  289. return true;
  290. }
  291. internal static bool IsToken(this string value)
  292. {
  293. foreach (char c in value)
  294. if (c < 0x20 || c >= 0x7f || _tspecials.Contains(c))
  295. return false;
  296. return true;
  297. }
  298. internal static string Quote(this string value)
  299. {
  300. return value.IsToken()
  301. ? value
  302. : String.Format("\"{0}\"", value.Replace("\"", "\\\""));
  303. }
  304. internal static byte[] ReadBytes(this Stream stream, int length)
  305. {
  306. return stream.readBytes(new byte[length], 0, length);
  307. }
  308. internal static byte[] ReadBytes(this Stream stream, long length, int bufferLength)
  309. {
  310. using (var result = new MemoryStream())
  311. {
  312. var count = length / bufferLength;
  313. var rem = (int)(length % bufferLength);
  314. var buffer = new byte[bufferLength];
  315. var end = false;
  316. for (long i = 0; i < count; i++)
  317. {
  318. if (!stream.readBytes(buffer, 0, bufferLength, result))
  319. {
  320. end = true;
  321. break;
  322. }
  323. }
  324. if (!end && rem > 0)
  325. stream.readBytes(new byte[rem], 0, rem, result);
  326. return result.ToArray();
  327. }
  328. }
  329. internal static async Task<byte[]> ReadBytesAsync(this Stream stream, int length)
  330. {
  331. var buffer = new byte[length];
  332. var len = await stream.ReadAsync(buffer, 0, length).ConfigureAwait(false);
  333. var bytes = len < 1
  334. ? new byte[0]
  335. : len < length
  336. ? stream.readBytes(buffer, len, length - len)
  337. : buffer;
  338. return bytes;
  339. }
  340. internal static string RemovePrefix(this string value, params string[] prefixes)
  341. {
  342. var i = 0;
  343. foreach (var prefix in prefixes)
  344. {
  345. if (value.StartsWith(prefix))
  346. {
  347. i = prefix.Length;
  348. break;
  349. }
  350. }
  351. return i > 0
  352. ? value.Substring(i)
  353. : value;
  354. }
  355. internal static T[] Reverse<T>(this T[] array)
  356. {
  357. var len = array.Length;
  358. T[] reverse = new T[len];
  359. var end = len - 1;
  360. for (var i = 0; i <= end; i++)
  361. reverse[i] = array[end - i];
  362. return reverse;
  363. }
  364. internal static IEnumerable<string> SplitHeaderValue(
  365. this string value, params char[] separator)
  366. {
  367. var len = value.Length;
  368. var separators = new string(separator);
  369. var buffer = new StringBuilder(32);
  370. var quoted = false;
  371. var escaped = false;
  372. char c;
  373. for (var i = 0; i < len; i++)
  374. {
  375. c = value[i];
  376. if (c == '"')
  377. {
  378. if (escaped)
  379. escaped = !escaped;
  380. else
  381. quoted = !quoted;
  382. }
  383. else if (c == '\\')
  384. {
  385. if (i < len - 1 && value[i + 1] == '"')
  386. escaped = true;
  387. }
  388. else if (separators.Contains(c))
  389. {
  390. if (!quoted)
  391. {
  392. yield return buffer.ToString();
  393. buffer.Length = 0;
  394. continue;
  395. }
  396. }
  397. else {
  398. }
  399. buffer.Append(c);
  400. }
  401. if (buffer.Length > 0)
  402. yield return buffer.ToString();
  403. }
  404. internal static byte[] ToByteArray(this Stream stream)
  405. {
  406. using (var output = new MemoryStream())
  407. {
  408. stream.Position = 0;
  409. stream.CopyTo(output);
  410. return output.ToArray();
  411. }
  412. }
  413. internal static byte[] ToByteArrayInternally(this ushort value, ByteOrder order)
  414. {
  415. var bytes = BitConverter.GetBytes(value);
  416. if (!order.IsHostOrder())
  417. Array.Reverse(bytes);
  418. return bytes;
  419. }
  420. internal static byte[] ToByteArrayInternally(this ulong value, ByteOrder order)
  421. {
  422. var bytes = BitConverter.GetBytes(value);
  423. if (!order.IsHostOrder())
  424. Array.Reverse(bytes);
  425. return bytes;
  426. }
  427. internal static string ToExtensionString(
  428. this CompressionMethod method, params string[] parameters)
  429. {
  430. if (method == CompressionMethod.None)
  431. return String.Empty;
  432. var m = String.Format("permessage-{0}", method.ToString().ToLower());
  433. if (parameters == null || parameters.Length == 0)
  434. return m;
  435. return String.Format("{0}; {1}", m, parameters.ToString("; "));
  436. }
  437. internal static List<TSource> ToList<TSource>(this IEnumerable<TSource> source)
  438. {
  439. return new List<TSource>(source);
  440. }
  441. internal static ushort ToUInt16(this byte[] src, ByteOrder srcOrder)
  442. {
  443. return BitConverter.ToUInt16(src.ToHostOrder(srcOrder), 0);
  444. }
  445. internal static ulong ToUInt64(this byte[] src, ByteOrder srcOrder)
  446. {
  447. return BitConverter.ToUInt64(src.ToHostOrder(srcOrder), 0);
  448. }
  449. internal static string TrimEndSlash(this string value)
  450. {
  451. value = value.TrimEnd('/');
  452. return value.Length > 0
  453. ? value
  454. : "/";
  455. }
  456. internal static string Unquote(this string value)
  457. {
  458. var start = value.IndexOf('\"');
  459. var end = value.LastIndexOf('\"');
  460. if (start < end)
  461. value = value.Substring(start + 1, end - start - 1).Replace("\\\"", "\"");
  462. return value.Trim();
  463. }
  464. internal static void WriteBytes(this Stream stream, byte[] value)
  465. {
  466. using (var src = new MemoryStream(value))
  467. {
  468. src.CopyTo(stream);
  469. }
  470. }
  471. #endregion
  472. #region Public Methods
  473. /// <summary>
  474. /// Determines whether the specified <see cref="string"/> contains any of characters
  475. /// in the specified array of <see cref="char"/>.
  476. /// </summary>
  477. /// <returns>
  478. /// <c>true</c> if <paramref name="value"/> contains any of <paramref name="chars"/>;
  479. /// otherwise, <c>false</c>.
  480. /// </returns>
  481. /// <param name="value">
  482. /// A <see cref="string"/> to test.
  483. /// </param>
  484. /// <param name="chars">
  485. /// An array of <see cref="char"/> that contains characters to find.
  486. /// </param>
  487. public static bool Contains(this string value, params char[] chars)
  488. {
  489. return chars == null || chars.Length == 0
  490. ? true
  491. : value == null || value.Length == 0
  492. ? false
  493. : value.IndexOfAny(chars) != -1;
  494. }
  495. /// <summary>
  496. /// Determines whether the specified <see cref="QueryParamCollection"/> contains the entry
  497. /// with the specified <paramref name="name"/>.
  498. /// </summary>
  499. /// <returns>
  500. /// <c>true</c> if <paramref name="collection"/> contains the entry
  501. /// with <paramref name="name"/>; otherwise, <c>false</c>.
  502. /// </returns>
  503. /// <param name="collection">
  504. /// A <see cref="QueryParamCollection"/> to test.
  505. /// </param>
  506. /// <param name="name">
  507. /// A <see cref="string"/> that represents the key of the entry to find.
  508. /// </param>
  509. public static bool Contains(this QueryParamCollection collection, string name)
  510. {
  511. return collection == null || collection.Count == 0
  512. ? false
  513. : collection[name] != null;
  514. }
  515. /// <summary>
  516. /// Determines whether the specified <see cref="QueryParamCollection"/> contains the entry
  517. /// with the specified both <paramref name="name"/> and <paramref name="value"/>.
  518. /// </summary>
  519. /// <returns>
  520. /// <c>true</c> if <paramref name="collection"/> contains the entry
  521. /// with both <paramref name="name"/> and <paramref name="value"/>;
  522. /// otherwise, <c>false</c>.
  523. /// </returns>
  524. /// <param name="collection">
  525. /// A <see cref="QueryParamCollection"/> to test.
  526. /// </param>
  527. /// <param name="name">
  528. /// A <see cref="string"/> that represents the key of the entry to find.
  529. /// </param>
  530. /// <param name="value">
  531. /// A <see cref="string"/> that represents the value of the entry to find.
  532. /// </param>
  533. public static bool Contains(this QueryParamCollection collection, string name, string value)
  534. {
  535. if (collection == null || collection.Count == 0)
  536. return false;
  537. var values = collection[name];
  538. if (values == null)
  539. return false;
  540. foreach (var v in values.Split(','))
  541. if (v.Trim().Equals(value, StringComparison.OrdinalIgnoreCase))
  542. return true;
  543. return false;
  544. }
  545. /// <summary>
  546. /// Emits the specified <see cref="EventHandler"/> delegate if it isn't <see langword="null"/>.
  547. /// </summary>
  548. /// <param name="eventHandler">
  549. /// A <see cref="EventHandler"/> to emit.
  550. /// </param>
  551. /// <param name="sender">
  552. /// An <see cref="object"/> from which emits this <paramref name="eventHandler"/>.
  553. /// </param>
  554. /// <param name="e">
  555. /// A <see cref="EventArgs"/> that contains no event data.
  556. /// </param>
  557. public static void Emit(this EventHandler eventHandler, object sender, EventArgs e)
  558. {
  559. if (eventHandler != null)
  560. eventHandler(sender, e);
  561. }
  562. /// <summary>
  563. /// Emits the specified <c>EventHandler&lt;TEventArgs&gt;</c> delegate
  564. /// if it isn't <see langword="null"/>.
  565. /// </summary>
  566. /// <param name="eventHandler">
  567. /// An <c>EventHandler&lt;TEventArgs&gt;</c> to emit.
  568. /// </param>
  569. /// <param name="sender">
  570. /// An <see cref="object"/> from which emits this <paramref name="eventHandler"/>.
  571. /// </param>
  572. /// <param name="e">
  573. /// A <c>TEventArgs</c> that represents the event data.
  574. /// </param>
  575. /// <typeparam name="TEventArgs">
  576. /// The type of the event data generated by the event.
  577. /// </typeparam>
  578. public static void Emit<TEventArgs>(
  579. this EventHandler<TEventArgs> eventHandler, object sender, TEventArgs e)
  580. where TEventArgs : EventArgs
  581. {
  582. if (eventHandler != null)
  583. eventHandler(sender, e);
  584. }
  585. /// <summary>
  586. /// Gets the collection of the HTTP cookies from the specified HTTP <paramref name="headers"/>.
  587. /// </summary>
  588. /// <returns>
  589. /// A <see cref="CookieCollection"/> that receives a collection of the HTTP cookies.
  590. /// </returns>
  591. /// <param name="headers">
  592. /// A <see cref="QueryParamCollection"/> that contains a collection of the HTTP headers.
  593. /// </param>
  594. /// <param name="response">
  595. /// <c>true</c> if <paramref name="headers"/> is a collection of the response headers;
  596. /// otherwise, <c>false</c>.
  597. /// </param>
  598. public static CookieCollection GetCookies(this QueryParamCollection headers, bool response)
  599. {
  600. var name = response ? "Set-Cookie" : "Cookie";
  601. return headers == null || !headers.Contains(name)
  602. ? new CookieCollection()
  603. : CookieHelper.Parse(headers[name], response);
  604. }
  605. /// <summary>
  606. /// Gets the description of the specified HTTP status <paramref name="code"/>.
  607. /// </summary>
  608. /// <returns>
  609. /// A <see cref="string"/> that represents the description of the HTTP status code.
  610. /// </returns>
  611. /// <param name="code">
  612. /// One of <see cref="HttpStatusCode"/> enum values, indicates the HTTP status codes.
  613. /// </param>
  614. public static string GetDescription(this HttpStatusCode code)
  615. {
  616. return ((int)code).GetStatusDescription();
  617. }
  618. /// <summary>
  619. /// Gets the name from the specified <see cref="string"/> that contains a pair of name and
  620. /// value separated by a separator string.
  621. /// </summary>
  622. /// <returns>
  623. /// A <see cref="string"/> that represents the name if any; otherwise, <c>null</c>.
  624. /// </returns>
  625. /// <param name="nameAndValue">
  626. /// A <see cref="string"/> that contains a pair of name and value separated by a separator
  627. /// string.
  628. /// </param>
  629. /// <param name="separator">
  630. /// A <see cref="string"/> that represents a separator string.
  631. /// </param>
  632. public static string GetName(this string nameAndValue, string separator)
  633. {
  634. return (nameAndValue != null && nameAndValue.Length > 0) &&
  635. (separator != null && separator.Length > 0)
  636. ? nameAndValue.GetNameInternal(separator)
  637. : null;
  638. }
  639. /// <summary>
  640. /// Gets the name and value from the specified <see cref="string"/> that contains a pair of
  641. /// name and value separated by a separator string.
  642. /// </summary>
  643. /// <returns>
  644. /// A <c>KeyValuePair&lt;string, string&gt;</c> that represents the name and value if any.
  645. /// </returns>
  646. /// <param name="nameAndValue">
  647. /// A <see cref="string"/> that contains a pair of name and value separated by a separator
  648. /// string.
  649. /// </param>
  650. /// <param name="separator">
  651. /// A <see cref="string"/> that represents a separator string.
  652. /// </param>
  653. public static KeyValuePair<string, string> GetNameAndValue(
  654. this string nameAndValue, string separator)
  655. {
  656. var name = nameAndValue.GetName(separator);
  657. var value = nameAndValue.GetValue(separator);
  658. return name != null
  659. ? new KeyValuePair<string, string>(name, value)
  660. : new KeyValuePair<string, string>(null, null);
  661. }
  662. /// <summary>
  663. /// Gets the description of the specified HTTP status <paramref name="code"/>.
  664. /// </summary>
  665. /// <returns>
  666. /// A <see cref="string"/> that represents the description of the HTTP status code.
  667. /// </returns>
  668. /// <param name="code">
  669. /// An <see cref="int"/> that represents the HTTP status code.
  670. /// </param>
  671. public static string GetStatusDescription(this int code)
  672. {
  673. switch (code)
  674. {
  675. case 100: return "Continue";
  676. case 101: return "Switching Protocols";
  677. case 102: return "Processing";
  678. case 200: return "OK";
  679. case 201: return "Created";
  680. case 202: return "Accepted";
  681. case 203: return "Non-Authoritative Information";
  682. case 204: return "No Content";
  683. case 205: return "Reset Content";
  684. case 206: return "Partial Content";
  685. case 207: return "Multi-Status";
  686. case 300: return "Multiple Choices";
  687. case 301: return "Moved Permanently";
  688. case 302: return "Found";
  689. case 303: return "See Other";
  690. case 304: return "Not Modified";
  691. case 305: return "Use Proxy";
  692. case 307: return "Temporary Redirect";
  693. case 400: return "Bad Request";
  694. case 401: return "Unauthorized";
  695. case 402: return "Payment Required";
  696. case 403: return "Forbidden";
  697. case 404: return "Not Found";
  698. case 405: return "Method Not Allowed";
  699. case 406: return "Not Acceptable";
  700. case 407: return "Proxy Authentication Required";
  701. case 408: return "Request Timeout";
  702. case 409: return "Conflict";
  703. case 410: return "Gone";
  704. case 411: return "Length Required";
  705. case 412: return "Precondition Failed";
  706. case 413: return "Request Entity Too Large";
  707. case 414: return "Request-Uri Too Long";
  708. case 415: return "Unsupported Media Type";
  709. case 416: return "Requested Range Not Satisfiable";
  710. case 417: return "Expectation Failed";
  711. case 422: return "Unprocessable Entity";
  712. case 423: return "Locked";
  713. case 424: return "Failed Dependency";
  714. case 500: return "Internal Server Error";
  715. case 501: return "Not Implemented";
  716. case 502: return "Bad Gateway";
  717. case 503: return "Service Unavailable";
  718. case 504: return "Gateway Timeout";
  719. case 505: return "Http Version Not Supported";
  720. case 507: return "Insufficient Storage";
  721. }
  722. return String.Empty;
  723. }
  724. /// <summary>
  725. /// Gets the value from the specified <see cref="string"/> that contains a pair of name and
  726. /// value separated by a separator string.
  727. /// </summary>
  728. /// <returns>
  729. /// A <see cref="string"/> that represents the value if any; otherwise, <c>null</c>.
  730. /// </returns>
  731. /// <param name="nameAndValue">
  732. /// A <see cref="string"/> that contains a pair of name and value separated by a separator
  733. /// string.
  734. /// </param>
  735. /// <param name="separator">
  736. /// A <see cref="string"/> that represents a separator string.
  737. /// </param>
  738. public static string GetValue(this string nameAndValue, string separator)
  739. {
  740. return (nameAndValue != null && nameAndValue.Length > 0) &&
  741. (separator != null && separator.Length > 0)
  742. ? nameAndValue.GetValueInternal(separator)
  743. : null;
  744. }
  745. /// <summary>
  746. /// Determines whether the specified <see cref="ByteOrder"/> is host
  747. /// (this computer architecture) byte order.
  748. /// </summary>
  749. /// <returns>
  750. /// <c>true</c> if <paramref name="order"/> is host byte order;
  751. /// otherwise, <c>false</c>.
  752. /// </returns>
  753. /// <param name="order">
  754. /// One of the <see cref="ByteOrder"/> enum values, to test.
  755. /// </param>
  756. public static bool IsHostOrder(this ByteOrder order)
  757. {
  758. // true : !(true ^ true) or !(false ^ false)
  759. // false: !(true ^ false) or !(false ^ true)
  760. return !(BitConverter.IsLittleEndian ^ (order == ByteOrder.Little));
  761. }
  762. /// <summary>
  763. /// Determines whether the specified <see cref="string"/> is a predefined scheme.
  764. /// </summary>
  765. /// <returns>
  766. /// <c>true</c> if <paramref name="value"/> is a predefined scheme; otherwise, <c>false</c>.
  767. /// </returns>
  768. /// <param name="value">
  769. /// A <see cref="string"/> to test.
  770. /// </param>
  771. public static bool IsPredefinedScheme(this string value)
  772. {
  773. if (value == null || value.Length < 2)
  774. return false;
  775. var c = value[0];
  776. if (c == 'h')
  777. return value == "http" || value == "https";
  778. if (c == 'w')
  779. return value == "ws" || value == "wss";
  780. if (c == 'f')
  781. return value == "file" || value == "ftp";
  782. if (c == 'n')
  783. {
  784. c = value[1];
  785. return c == 'e'
  786. ? value == "news" || value == "net.pipe" || value == "net.tcp"
  787. : value == "nntp";
  788. }
  789. return (c == 'g' && value == "gopher") || (c == 'm' && value == "mailto");
  790. }
  791. /// <summary>
  792. /// Determines whether the specified <see cref="string"/> is a URI string.
  793. /// </summary>
  794. /// <returns>
  795. /// <c>true</c> if <paramref name="value"/> may be a URI string; otherwise, <c>false</c>.
  796. /// </returns>
  797. /// <param name="value">
  798. /// A <see cref="string"/> to test.
  799. /// </param>
  800. public static bool MaybeUri(this string value)
  801. {
  802. if (value == null || value.Length == 0)
  803. return false;
  804. var i = value.IndexOf(':');
  805. if (i == -1)
  806. return false;
  807. if (i >= 10)
  808. return false;
  809. return value.Substring(0, i).IsPredefinedScheme();
  810. }
  811. /// <summary>
  812. /// Retrieves a sub-array from the specified <paramref name="array"/>.
  813. /// A sub-array starts at the specified element position.
  814. /// </summary>
  815. /// <returns>
  816. /// An array of T that receives a sub-array, or an empty array of T if any problems
  817. /// with the parameters.
  818. /// </returns>
  819. /// <param name="array">
  820. /// An array of T that contains the data to retrieve a sub-array.
  821. /// </param>
  822. /// <param name="startIndex">
  823. /// An <see cref="int"/> that contains the zero-based starting position of a sub-array
  824. /// in <paramref name="array"/>.
  825. /// </param>
  826. /// <param name="length">
  827. /// An <see cref="int"/> that contains the number of elements to retrieve a sub-array.
  828. /// </param>
  829. /// <typeparam name="T">
  830. /// The type of elements in the <paramref name="array"/>.
  831. /// </typeparam>
  832. public static T[] SubArray<T>(this T[] array, int startIndex, int length)
  833. {
  834. if (array == null || array.Length == 0)
  835. return new T[0];
  836. if (startIndex < 0 || length <= 0)
  837. return new T[0];
  838. if (startIndex + length > array.Length)
  839. return new T[0];
  840. if (startIndex == 0 && array.Length == length)
  841. return array;
  842. T[] subArray = new T[length];
  843. Array.Copy(array, startIndex, subArray, 0, length);
  844. return subArray;
  845. }
  846. /// <summary>
  847. /// Converts the order of the specified array of <see cref="byte"/> to the host byte order.
  848. /// </summary>
  849. /// <returns>
  850. /// An array of <see cref="byte"/> converted from <paramref name="src"/>.
  851. /// </returns>
  852. /// <param name="src">
  853. /// An array of <see cref="byte"/> to convert.
  854. /// </param>
  855. /// <param name="srcOrder">
  856. /// One of the <see cref="ByteOrder"/> enum values, indicates the byte order of
  857. /// <paramref name="src"/>.
  858. /// </param>
  859. /// <exception cref="ArgumentNullException">
  860. /// <paramref name="src"/> is <see langword="null"/>.
  861. /// </exception>
  862. public static byte[] ToHostOrder(this byte[] src, ByteOrder srcOrder)
  863. {
  864. if (src == null)
  865. throw new ArgumentNullException("src");
  866. return src.Length > 1 && !srcOrder.IsHostOrder()
  867. ? src.Reverse()
  868. : src;
  869. }
  870. /// <summary>
  871. /// Converts the specified <paramref name="array"/> to a <see cref="string"/> that
  872. /// concatenates the each element of <paramref name="array"/> across the specified
  873. /// <paramref name="separator"/>.
  874. /// </summary>
  875. /// <returns>
  876. /// A <see cref="string"/> converted from <paramref name="array"/>,
  877. /// or <see cref="String.Empty"/> if <paramref name="array"/> is empty.
  878. /// </returns>
  879. /// <param name="array">
  880. /// An array of T to convert.
  881. /// </param>
  882. /// <param name="separator">
  883. /// A <see cref="string"/> that represents the separator string.
  884. /// </param>
  885. /// <typeparam name="T">
  886. /// The type of elements in <paramref name="array"/>.
  887. /// </typeparam>
  888. /// <exception cref="ArgumentNullException">
  889. /// <paramref name="array"/> is <see langword="null"/>.
  890. /// </exception>
  891. public static string ToString<T>(this T[] array, string separator)
  892. {
  893. if (array == null)
  894. throw new ArgumentNullException("array");
  895. var len = array.Length;
  896. if (len == 0)
  897. return String.Empty;
  898. if (separator == null)
  899. separator = String.Empty;
  900. var buff = new StringBuilder(64);
  901. (len - 1).Times(i => buff.AppendFormat("{0}{1}", array[i].ToString(), separator));
  902. buff.Append(array[len - 1].ToString());
  903. return buff.ToString();
  904. }
  905. /// <summary>
  906. /// Executes the specified <c>Action&lt;int&gt;</c> delegate <paramref name="n"/> times.
  907. /// </summary>
  908. /// <param name="n">
  909. /// An <see cref="int"/> is the number of times to execute.
  910. /// </param>
  911. /// <param name="action">
  912. /// An <c>Action&lt;int&gt;</c> delegate that references the method(s) to execute.
  913. /// An <see cref="int"/> parameter to pass to the method(s) is the zero-based count of
  914. /// iteration.
  915. /// </param>
  916. public static void Times(this int n, Action<int> action)
  917. {
  918. if (n > 0 && action != null)
  919. for (int i = 0; i < n; i++)
  920. action(i);
  921. }
  922. /// <summary>
  923. /// Converts the specified <see cref="string"/> to a <see cref="Uri"/>.
  924. /// </summary>
  925. /// <returns>
  926. /// A <see cref="Uri"/> converted from <paramref name="uriString"/>, or <see langword="null"/>
  927. /// if <paramref name="uriString"/> isn't successfully converted.
  928. /// </returns>
  929. /// <param name="uriString">
  930. /// A <see cref="string"/> to convert.
  931. /// </param>
  932. public static Uri ToUri(this string uriString)
  933. {
  934. Uri res;
  935. return Uri.TryCreate(
  936. uriString, uriString.MaybeUri() ? UriKind.Absolute : UriKind.Relative, out res)
  937. ? res
  938. : null;
  939. }
  940. /// <summary>
  941. /// URL-decodes the specified <see cref="string"/>.
  942. /// </summary>
  943. /// <returns>
  944. /// A <see cref="string"/> that receives the decoded string, or the <paramref name="value"/>
  945. /// if it's <see langword="null"/> or empty.
  946. /// </returns>
  947. /// <param name="value">
  948. /// A <see cref="string"/> to decode.
  949. /// </param>
  950. public static string UrlDecode(this string value)
  951. {
  952. return value == null || value.Length == 0
  953. ? value
  954. : WebUtility.UrlDecode(value);
  955. }
  956. #endregion
  957. }
  958. }