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.
385 lines
12 KiB
385 lines
12 KiB
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Specialized;
|
|
|
|
namespace Apewer.Network
|
|
{
|
|
|
|
/// <summary>HTTP 头的集合。</summary>
|
|
[Serializable]
|
|
public sealed class HttpHeaders : IEnumerable<HttpHeader>, ICollection<HttpHeader>, IToJson
|
|
{
|
|
|
|
#region IEnumerable
|
|
|
|
List<HttpHeader> _list = new List<HttpHeader>();
|
|
long _version = 0L;
|
|
|
|
/// <summary>获取枚举器。</summary>
|
|
public IEnumerator<HttpHeader> GetEnumerator() => new Enumerator(this);
|
|
|
|
/// <summary>获取枚举器。</summary>
|
|
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
|
|
|
|
/// <summary></summary>
|
|
public sealed class Enumerator : IEnumerator<HttpHeader>
|
|
{
|
|
|
|
HttpHeaders _headeres = null;
|
|
long _version = 0L;
|
|
bool _disposed = false;
|
|
|
|
HttpHeader _current = null;
|
|
int _index = 0;
|
|
|
|
const string ObjectName = nameof(Enumerator);
|
|
const string OriginChanged = "原集合已变更,无法继续遍历。";
|
|
|
|
/// <summary></summary>
|
|
public HttpHeader Current
|
|
{
|
|
get
|
|
{
|
|
if (_disposed) throw new ObjectDisposedException(ObjectName);
|
|
if (_version != _headeres._version) throw new InvalidOperationException(OriginChanged);
|
|
return _current;
|
|
}
|
|
}
|
|
|
|
/// <summary></summary>
|
|
object IEnumerator.Current { get => Current; }
|
|
|
|
/// <summary></summary>
|
|
public void Dispose() { _disposed = true; }
|
|
|
|
/// <summary></summary>
|
|
public bool MoveNext()
|
|
{
|
|
if (_disposed) throw new ObjectDisposedException(ObjectName);
|
|
if (_version != _headeres._version) throw new InvalidOperationException(OriginChanged);
|
|
|
|
if (_index < _headeres._list.Count)
|
|
{
|
|
_current = _headeres._list[_index];
|
|
_index++;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary></summary>
|
|
public void Reset()
|
|
{
|
|
_current = null;
|
|
_index = 0;
|
|
}
|
|
|
|
/// <exception cref="ArgumentNullException" />
|
|
public Enumerator(HttpHeaders headers)
|
|
{
|
|
if (headers == null) throw new ArgumentNullException(nameof(headers));
|
|
_headeres = headers;
|
|
_version = headers._version;
|
|
Reset();
|
|
}
|
|
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ICollection
|
|
|
|
/// <summary>元素数量。</summary>
|
|
public int Count { get => _list.Count; }
|
|
|
|
/// <summary>当前集合是只读。</summary>
|
|
public bool IsReadOnly { get => false; }
|
|
|
|
/// <summary>添加一项。</summary>
|
|
/// <exception cref="ArgumentNullException" />
|
|
public HttpHeader Add(string name, string value)
|
|
{
|
|
if (name.IsEmpty()) throw new ArgumentNullException(nameof(name));
|
|
var header = new HttpHeader(name, value);
|
|
_list.Add(header);
|
|
_version++;
|
|
return header;
|
|
}
|
|
|
|
/// <summary>添加一项。</summary>
|
|
/// <exception cref="ArgumentNullException" />
|
|
public void Add(HttpHeader header)
|
|
{
|
|
if (header == null) throw new ArgumentNullException(nameof(header));
|
|
_list.Add(header);
|
|
_version++;
|
|
}
|
|
|
|
/// <summary>移除所有元素。</summary>
|
|
public void Clear() => _list.Clear();
|
|
|
|
/// <summary>判断是否包含指定的元素。</summary>
|
|
public bool Contains(HttpHeader item) => _list.Contains(item);
|
|
|
|
/// <summary>复制所有元素到数组。</summary>
|
|
/// <param name="array">目标数组。</param>
|
|
/// <param name="arrayIndex">数组的位置。</param>
|
|
/// <exception cref="ArgumentNullException" />
|
|
/// <exception cref="ArgumentOutOfRangeException" />
|
|
/// <exception cref="ArgumentException" />
|
|
public void CopyTo(HttpHeader[] array, int arrayIndex) => _list.CopyTo(array, arrayIndex);
|
|
|
|
/// <summary>移除指定的元素</summary>
|
|
public bool Remove(HttpHeader item) => _list.Remove(item);
|
|
|
|
#endregion
|
|
|
|
#region IList
|
|
|
|
/// <summary></summary>
|
|
/// <exception cref="ArgumentOutOfRangeException" />
|
|
public HttpHeader this[int index] { get => _list[index]; set => _list[index] = value; }
|
|
|
|
/// <summary></summary>
|
|
/// <exception cref="ArgumentOutOfRangeException" />
|
|
public string this[string name] { get => GetValue(name); set => SetValue(name, value); }
|
|
|
|
/// <summary>搜索指定对象在当前集合中的位置索引。</summary>
|
|
public int IndexOf(HttpHeader item) => _list.IndexOf(item);
|
|
|
|
/// <summary>插入元素到指定位置。</summary>
|
|
/// <exception cref="ArgumentOutOfRangeException" />
|
|
public void Insert(int index, HttpHeader item) => _list.Insert(index, item);
|
|
|
|
/// <summary>移除指定位置的元素。</summary>
|
|
/// <exception cref="ArgumentOutOfRangeException" />
|
|
public void RemoveAt(int index) => _list.RemoveAt(index);
|
|
|
|
#endregion
|
|
|
|
#region constructor
|
|
|
|
/// <summary></summary>
|
|
public HttpHeaders() { }
|
|
|
|
/// <summary></summary>
|
|
/// <exception cref="ArgumentNullException" />
|
|
public HttpHeaders(params HttpHeader[] headers) : this(headers as IEnumerable<HttpHeader>) { }
|
|
|
|
/// <summary></summary>
|
|
/// <exception cref="ArgumentNullException" />
|
|
public HttpHeaders(IEnumerable<HttpHeader> headers)
|
|
{
|
|
if (headers == null) throw new ArgumentNullException(nameof(headers));
|
|
|
|
foreach (var header in headers)
|
|
{
|
|
if (header == null) continue;
|
|
if (header.Name.IsEmpty()) continue;
|
|
Add(header);
|
|
}
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <exception cref="ArgumentNullException" />
|
|
public HttpHeaders(IEnumerable<KeyValuePair<string, string>> headers)
|
|
{
|
|
if (headers == null) return;
|
|
|
|
foreach (var header in headers)
|
|
{
|
|
if (header.Key.IsEmpty()) continue;
|
|
Add(header.Key, header.Value);
|
|
}
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <exception cref="ArgumentNullException" />
|
|
public HttpHeaders(NameValueCollection headers)
|
|
{
|
|
if (headers == null) return;
|
|
|
|
var keys = headers.AllKeys;
|
|
foreach (var key in keys)
|
|
{
|
|
if (key.IsEmpty()) continue;
|
|
var value = headers[key];
|
|
Add(key, value);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region operation
|
|
|
|
/// <summary>获取所有名称。</summary>
|
|
public string[] GetNames()
|
|
{
|
|
var names = new List<string>();
|
|
var count = _list.Count;
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
var name = _list[i].Name;
|
|
if (string.IsNullOrEmpty(name)) continue;
|
|
if (names.Contains(name)) continue;
|
|
names.Add(name);
|
|
}
|
|
names.Sort();
|
|
return names.ToArray();
|
|
}
|
|
|
|
/// <summary>获取匹配 Name 的 Value。不存在 Name 时返回 NULL 值。</summary>
|
|
/// <exception cref="ArgumentNullException" />
|
|
public string GetValue(string name)
|
|
{
|
|
if (name.IsEmpty()) throw new ArgumentNullException(nameof(name));
|
|
|
|
// 精准匹配。
|
|
var count = _list.Count;
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
var item = _list[i];
|
|
if (string.IsNullOrEmpty(item.Name)) continue;
|
|
if (item.Name == name)
|
|
{
|
|
if (string.IsNullOrEmpty(item.Value)) continue;
|
|
return item.Value;
|
|
}
|
|
}
|
|
|
|
// 忽略大小写。
|
|
var lower = TextUtility.Lower(name);
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
var item = _list[i];
|
|
if (string.IsNullOrEmpty(item.Name)) continue;
|
|
if (TextUtility.Lower(item.Name) == lower)
|
|
{
|
|
if (string.IsNullOrEmpty(item.Value)) continue;
|
|
return item.Value;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>获取匹配 Name 的 Value。不存在 Name 时返回 NULL 值。</summary>
|
|
/// <exception cref="ArgumentNullException" />
|
|
public string[] GetValues(string name)
|
|
{
|
|
if (name.IsEmpty()) throw new ArgumentNullException(nameof(name));
|
|
|
|
// 忽略大小写。
|
|
// name = TextUtility.Lower(name);
|
|
|
|
var count = _list.Count;
|
|
var values = new List<string>(_list.Count);
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
var item = _list[i];
|
|
if (string.IsNullOrEmpty(item.Name)) continue;
|
|
if (string.Equals(item.Name, name, StringComparison.CurrentCultureIgnoreCase))
|
|
{
|
|
var value = item.Value.ToTrim();
|
|
if (string.IsNullOrEmpty(value)) continue;
|
|
values.Add(value);
|
|
}
|
|
}
|
|
|
|
return values.ToArray();
|
|
}
|
|
|
|
/// <summary>设置 Name 的 Value。不存在 Name 时添加新元素。</summary>
|
|
/// <exception cref="ArgumentNullException" />
|
|
public HttpHeader SetValue(string name, string value)
|
|
{
|
|
if (name.IsEmpty()) throw new ArgumentNullException(nameof(name));
|
|
|
|
// 尝试精准匹配。
|
|
var count = _list.Count;
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
var item = _list[i];
|
|
if (string.IsNullOrEmpty(item.Name)) continue;
|
|
if (item.Name == name)
|
|
{
|
|
item.Value = value;
|
|
_version++;
|
|
return item;
|
|
}
|
|
}
|
|
|
|
// 尝试模糊匹配。
|
|
var lower = TextUtility.Lower(name);
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
var item = _list[i];
|
|
if (TextUtility.Lower(item.Name) == lower)
|
|
{
|
|
item.Value = value;
|
|
_version++;
|
|
return item;
|
|
}
|
|
}
|
|
|
|
// 添加新项。
|
|
return Add(name, value);
|
|
}
|
|
|
|
/// <summary>每个元素组成为新数组。</summary>
|
|
public HttpHeader[] ToArray() => _list.ToArray();
|
|
|
|
#endregion
|
|
|
|
#region Json
|
|
|
|
internal HttpHeaders(Json json)
|
|
{
|
|
if (!json) return;
|
|
|
|
if (json.IsObject)
|
|
{
|
|
var properties = json.GetProperties();
|
|
foreach (var property in properties)
|
|
{
|
|
if (property == null) continue;
|
|
if (property.Name.IsEmpty()) continue;
|
|
Add(property.Name, property.Value?.ToString());
|
|
}
|
|
}
|
|
|
|
if (json.IsArray)
|
|
{
|
|
var array = json.Array<HttpHeader>();
|
|
if (array != null)
|
|
{
|
|
for (var i = 0; i < array.Length; i++)
|
|
{
|
|
if (array[i] == null) continue;
|
|
if (array[i].Name.IsEmpty()) continue;
|
|
_list.Add(array[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary></summary>
|
|
public Json ToJson()
|
|
{
|
|
var array = Json.NewArray();
|
|
var count = 0;
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
var item = Json.From(_list[i]);
|
|
array.AddItem(item);
|
|
}
|
|
return array;
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
|
|
}
|
|
|