You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

408 lines
13 KiB

8 months ago
6 months ago
8 months ago
8 months ago
8 months ago
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Collections.Specialized;
  5. namespace Apewer.Network
  6. {
  7. /// <summary>HTTP 头的集合。</summary>
  8. [Serializable]
  9. public sealed class HttpHeaders : IEnumerable<HttpHeader>, ICollection<HttpHeader>, IToJson
  10. {
  11. #region IEnumerable
  12. List<HttpHeader> _list = new List<HttpHeader>();
  13. long _version = 0L;
  14. /// <summary>获取枚举器。</summary>
  15. public IEnumerator<HttpHeader> GetEnumerator() => new Enumerator(this);
  16. /// <summary>获取枚举器。</summary>
  17. IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
  18. /// <summary></summary>
  19. public sealed class Enumerator : IEnumerator<HttpHeader>
  20. {
  21. HttpHeaders _headeres = null;
  22. long _version = 0L;
  23. bool _disposed = false;
  24. HttpHeader _current = null;
  25. int _index = 0;
  26. const string ObjectName = nameof(Enumerator);
  27. const string OriginChanged = "原集合已变更,无法继续遍历。";
  28. /// <summary></summary>
  29. public HttpHeader Current
  30. {
  31. get
  32. {
  33. if (_disposed) throw new ObjectDisposedException(ObjectName);
  34. if (_version != _headeres._version) throw new InvalidOperationException(OriginChanged);
  35. return _current;
  36. }
  37. }
  38. /// <summary></summary>
  39. object IEnumerator.Current { get => Current; }
  40. /// <summary></summary>
  41. public void Dispose() { _disposed = true; }
  42. /// <summary></summary>
  43. public bool MoveNext()
  44. {
  45. if (_disposed) throw new ObjectDisposedException(ObjectName);
  46. if (_version != _headeres._version) throw new InvalidOperationException(OriginChanged);
  47. if (_index < _headeres._list.Count)
  48. {
  49. _current = _headeres._list[_index];
  50. _index++;
  51. return true;
  52. }
  53. return false;
  54. }
  55. /// <summary></summary>
  56. public void Reset()
  57. {
  58. _current = null;
  59. _index = 0;
  60. }
  61. /// <exception cref="ArgumentNullException" />
  62. public Enumerator(HttpHeaders headers)
  63. {
  64. if (headers == null) throw new ArgumentNullException(nameof(headers));
  65. _headeres = headers;
  66. _version = headers._version;
  67. Reset();
  68. }
  69. }
  70. #endregion
  71. #region ICollection
  72. /// <summary>元素数量。</summary>
  73. public int Count { get => _list.Count; }
  74. /// <summary>当前集合是只读。</summary>
  75. public bool IsReadOnly { get => false; }
  76. /// <summary>添加一项。</summary>
  77. /// <exception cref="ArgumentNullException" />
  78. public HttpHeader Add(string name, string value)
  79. {
  80. if (name.IsEmpty()) throw new ArgumentNullException(nameof(name));
  81. var header = new HttpHeader(name, value);
  82. _list.Add(header);
  83. _version++;
  84. return header;
  85. }
  86. /// <summary>添加一项。</summary>
  87. /// <exception cref="ArgumentNullException" />
  88. public void Add(HttpHeader header)
  89. {
  90. if (header == null) throw new ArgumentNullException(nameof(header));
  91. _list.Add(header);
  92. _version++;
  93. }
  94. /// <summary>移除所有元素。</summary>
  95. public void Clear() => _list.Clear();
  96. /// <summary>判断是否包含指定的元素。</summary>
  97. public bool Contains(HttpHeader item) => _list.Contains(item);
  98. /// <summary>复制所有元素到数组。</summary>
  99. /// <param name="array">目标数组。</param>
  100. /// <param name="arrayIndex">数组的位置。</param>
  101. /// <exception cref="ArgumentNullException" />
  102. /// <exception cref="ArgumentOutOfRangeException" />
  103. /// <exception cref="ArgumentException" />
  104. public void CopyTo(HttpHeader[] array, int arrayIndex) => _list.CopyTo(array, arrayIndex);
  105. /// <summary>移除指定的元素</summary>
  106. public bool Remove(HttpHeader item) => _list.Remove(item);
  107. #endregion
  108. #region IList
  109. /// <summary></summary>
  110. /// <exception cref="ArgumentOutOfRangeException" />
  111. public HttpHeader this[int index] { get => _list[index]; set => _list[index] = value; }
  112. /// <summary></summary>
  113. /// <exception cref="ArgumentOutOfRangeException" />
  114. public string this[string name] { get => GetValue(name); set => SetValue(name, value); }
  115. /// <summary>搜索指定对象在当前集合中的位置索引。</summary>
  116. public int IndexOf(HttpHeader item) => _list.IndexOf(item);
  117. /// <summary>插入元素到指定位置。</summary>
  118. /// <exception cref="ArgumentOutOfRangeException" />
  119. public void Insert(int index, HttpHeader item) => _list.Insert(index, item);
  120. /// <summary>移除指定位置的元素。</summary>
  121. /// <exception cref="ArgumentOutOfRangeException" />
  122. public void RemoveAt(int index) => _list.RemoveAt(index);
  123. #endregion
  124. #region constructor
  125. /// <summary></summary>
  126. public HttpHeaders() { }
  127. /// <summary></summary>
  128. /// <exception cref="ArgumentNullException" />
  129. public HttpHeaders(params HttpHeader[] headers) : this(headers as IEnumerable<HttpHeader>) { }
  130. /// <summary></summary>
  131. /// <exception cref="ArgumentNullException" />
  132. public HttpHeaders(IEnumerable<HttpHeader> headers)
  133. {
  134. if (headers == null) throw new ArgumentNullException(nameof(headers));
  135. foreach (var header in headers)
  136. {
  137. if (header == null) continue;
  138. if (header.Name.IsEmpty()) continue;
  139. Add(header);
  140. }
  141. }
  142. /// <summary></summary>
  143. /// <exception cref="ArgumentNullException" />
  144. public HttpHeaders(IEnumerable<KeyValuePair<string, string>> headers)
  145. {
  146. if (headers == null) return;
  147. foreach (var header in headers)
  148. {
  149. if (header.Key.IsEmpty()) continue;
  150. Add(header.Key, header.Value);
  151. }
  152. }
  153. /// <summary></summary>
  154. /// <exception cref="ArgumentNullException" />
  155. public HttpHeaders(NameValueCollection headers)
  156. {
  157. if (headers == null) return;
  158. var keys = headers.AllKeys;
  159. foreach (var key in keys)
  160. {
  161. if (key.IsEmpty()) continue;
  162. var value = headers[key];
  163. Add(key, value);
  164. }
  165. }
  166. #endregion
  167. #region operation
  168. /// <summary>获取所有名称。</summary>
  169. public string[] GetNames()
  170. {
  171. var names = new List<string>();
  172. var count = _list.Count;
  173. for (var i = 0; i < count; i++)
  174. {
  175. var name = _list[i].Name;
  176. if (string.IsNullOrEmpty(name)) continue;
  177. if (names.Contains(name)) continue;
  178. names.Add(name);
  179. }
  180. names.Sort();
  181. return names.ToArray();
  182. }
  183. /// <summary>获取匹配 Name 的 Value。不存在 Name 时返回 NULL 值。</summary>
  184. /// <exception cref="ArgumentNullException" />
  185. public string GetValue(string name)
  186. {
  187. if (name.IsEmpty()) throw new ArgumentNullException(nameof(name));
  188. // 精准匹配。
  189. var count = _list.Count;
  190. for (var i = 0; i < count; i++)
  191. {
  192. var item = _list[i];
  193. if (string.IsNullOrEmpty(item.Name)) continue;
  194. if (item.Name == name)
  195. {
  196. if (string.IsNullOrEmpty(item.Value)) continue;
  197. return item.Value;
  198. }
  199. }
  200. // 忽略大小写。
  201. var lower = TextUtility.Lower(name);
  202. for (var i = 0; i < count; i++)
  203. {
  204. var item = _list[i];
  205. if (string.IsNullOrEmpty(item.Name)) continue;
  206. if (TextUtility.Lower(item.Name) == lower)
  207. {
  208. if (string.IsNullOrEmpty(item.Value)) continue;
  209. return item.Value;
  210. }
  211. }
  212. return null;
  213. }
  214. /// <summary>获取匹配 Name 的 Value。不存在 Name 时返回 NULL 值。</summary>
  215. /// <exception cref="ArgumentNullException" />
  216. public string[] GetValues(string name)
  217. {
  218. if (name.IsEmpty()) throw new ArgumentNullException(nameof(name));
  219. // 忽略大小写。
  220. // name = TextUtility.Lower(name);
  221. var count = _list.Count;
  222. var values = new List<string>(_list.Count);
  223. for (var i = 0; i < count; i++)
  224. {
  225. var item = _list[i];
  226. if (string.IsNullOrEmpty(item.Name)) continue;
  227. if (string.Equals(item.Name, name, StringComparison.CurrentCultureIgnoreCase))
  228. {
  229. var value = item.Value.ToTrim();
  230. if (string.IsNullOrEmpty(value)) continue;
  231. values.Add(value);
  232. }
  233. }
  234. return values.ToArray();
  235. }
  236. /// <summary>设置 Name 的 Value。不存在 Name 时添加新元素。</summary>
  237. /// <exception cref="ArgumentNullException" />
  238. public HttpHeader SetValue(string name, string value)
  239. {
  240. if (name.IsEmpty()) throw new ArgumentNullException(nameof(name));
  241. // 尝试精准匹配。
  242. var count = _list.Count;
  243. for (var i = 0; i < count; i++)
  244. {
  245. var item = _list[i];
  246. if (string.IsNullOrEmpty(item.Name)) continue;
  247. if (item.Name == name)
  248. {
  249. item.Value = value;
  250. _version++;
  251. return item;
  252. }
  253. }
  254. // 尝试模糊匹配。
  255. var lower = TextUtility.Lower(name);
  256. for (var i = 0; i < count; i++)
  257. {
  258. var item = _list[i];
  259. if (TextUtility.Lower(item.Name) == lower)
  260. {
  261. item.Value = value;
  262. _version++;
  263. return item;
  264. }
  265. }
  266. // 添加新项。
  267. return Add(name, value);
  268. }
  269. /// <summary>按名称排序。</summary>
  270. public void Sort() => _list.Sort();
  271. /// <summary>按指定的方式排序。</summary>
  272. public void Sort(IComparer<HttpHeader> comparer) => _list.Sort(comparer ?? throw new ArgumentNullException(nameof(comparer)));
  273. /// <summary>按指定的方式排序。</summary>
  274. public void Sort(Comparison<HttpHeader> comparison) => _list.Sort(comparison ?? throw new ArgumentNullException(nameof(comparison)));
  275. /// <summary>每个元素组成为新数组。</summary>
  276. public HttpHeader[] ToArray() => _list.ToArray();
  277. /// <summary>生成 <see cref="StringPairs"/> 数组。</summary>
  278. public StringPairs ToStringParis()
  279. {
  280. var sp = new StringPairs();
  281. foreach (var item in _list) sp.Add(item.Name, item.Value);
  282. return sp;
  283. }
  284. /// <summary></summary>
  285. public override string ToString() => $"Count = {_list.Count}";
  286. /// <summary></summary>
  287. public static implicit operator StringPairs(HttpHeaders headers) => headers?.ToStringParis();
  288. #endregion
  289. #region Json
  290. internal HttpHeaders(Json json)
  291. {
  292. if (!json) return;
  293. if (json.IsObject)
  294. {
  295. var properties = json.GetProperties();
  296. foreach (var property in properties)
  297. {
  298. if (property == null) continue;
  299. if (property.Name.IsEmpty()) continue;
  300. Add(property.Name, property.Value?.ToString());
  301. }
  302. }
  303. if (json.IsArray)
  304. {
  305. var array = json.Array<HttpHeader>();
  306. if (array != null)
  307. {
  308. for (var i = 0; i < array.Length; i++)
  309. {
  310. if (array[i] == null) continue;
  311. if (array[i].Name.IsEmpty()) continue;
  312. _list.Add(array[i]);
  313. }
  314. }
  315. }
  316. }
  317. /// <summary></summary>
  318. public Json ToJson()
  319. {
  320. var array = Json.NewArray();
  321. var count = 0;
  322. for (var i = 0; i < count; i++)
  323. {
  324. var item = Json.From(_list[i]);
  325. array.AddItem(item);
  326. }
  327. return array;
  328. }
  329. #endregion
  330. }
  331. }