|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
namespace System.Net.Http.Headers
{
public sealed class HttpResponseHeaders : HttpHeaders
{
private const int AcceptRangesSlot = 0;
private const int ProxyAuthenticateSlot = 1;
private const int ServerSlot = 2;
private const int VarySlot = 3;
private const int WwwAuthenticateSlot = 4;
private const int NumCollectionsSlots = 5;
private object[]? _specialCollectionsSlots;
private HttpGeneralHeaders? _generalHeaders;
private readonly bool _containsTrailingHeaders;
#region Response Headers
private T GetSpecializedCollection<T>(int slot, Func<HttpResponseHeaders, T> creationFunc)
{
// 5 properties each lazily allocate a collection to store the value(s) for that property.
// Rather than having a field for each of these, store them untyped in an array that's lazily
// allocated. Then we only pay for the 45 bytes for those fields when any is actually accessed.
object[] collections = _specialCollectionsSlots ??= new object[NumCollectionsSlots];
return (T)(collections[slot] ??= creationFunc(this)!);
}
public HttpHeaderValueCollection<string> AcceptRanges =>
GetSpecializedCollection(AcceptRangesSlot, static thisRef => new HttpHeaderValueCollection<string>(KnownHeaders.AcceptRanges.Descriptor, thisRef));
public TimeSpan? Age
{
get { return HeaderUtilities.GetTimeSpanValue(KnownHeaders.Age.Descriptor, this); }
set { SetOrRemoveParsedValue(KnownHeaders.Age.Descriptor, value); }
}
public EntityTagHeaderValue? ETag
{
get { return (EntityTagHeaderValue?)GetSingleParsedValue(KnownHeaders.ETag.Descriptor); }
set { SetOrRemoveParsedValue(KnownHeaders.ETag.Descriptor, value); }
}
public Uri? Location
{
get { return (Uri?)GetSingleParsedValue(KnownHeaders.Location.Descriptor); }
set { SetOrRemoveParsedValue(KnownHeaders.Location.Descriptor, value); }
}
public HttpHeaderValueCollection<AuthenticationHeaderValue> ProxyAuthenticate =>
GetSpecializedCollection(ProxyAuthenticateSlot, static thisRef => new HttpHeaderValueCollection<AuthenticationHeaderValue>(KnownHeaders.ProxyAuthenticate.Descriptor, thisRef));
public RetryConditionHeaderValue? RetryAfter
{
get { return (RetryConditionHeaderValue?)GetSingleParsedValue(KnownHeaders.RetryAfter.Descriptor); }
set { SetOrRemoveParsedValue(KnownHeaders.RetryAfter.Descriptor, value); }
}
public HttpHeaderValueCollection<ProductInfoHeaderValue> Server =>
GetSpecializedCollection(ServerSlot, static thisRef => new HttpHeaderValueCollection<ProductInfoHeaderValue>(KnownHeaders.Server.Descriptor, thisRef));
public HttpHeaderValueCollection<string> Vary =>
GetSpecializedCollection(VarySlot, static thisRef => new HttpHeaderValueCollection<string>(KnownHeaders.Vary.Descriptor, thisRef));
public HttpHeaderValueCollection<AuthenticationHeaderValue> WwwAuthenticate =>
GetSpecializedCollection(WwwAuthenticateSlot, static thisRef => new HttpHeaderValueCollection<AuthenticationHeaderValue>(KnownHeaders.WWWAuthenticate.Descriptor, thisRef));
#endregion
#region General Headers
public CacheControlHeaderValue? CacheControl
{
get { return GeneralHeaders.CacheControl; }
set { GeneralHeaders.CacheControl = value; }
}
public HttpHeaderValueCollection<string> Connection
{
get { return GeneralHeaders.Connection; }
}
public bool? ConnectionClose
{
get { return HttpGeneralHeaders.GetConnectionClose(this, _generalHeaders); } // special-cased to avoid forcing _generalHeaders initialization
set { GeneralHeaders.ConnectionClose = value; }
}
public DateTimeOffset? Date
{
get { return GeneralHeaders.Date; }
set { GeneralHeaders.Date = value; }
}
public HttpHeaderValueCollection<NameValueHeaderValue> Pragma
{
get { return GeneralHeaders.Pragma; }
}
public HttpHeaderValueCollection<string> Trailer
{
get { return GeneralHeaders.Trailer; }
}
public HttpHeaderValueCollection<TransferCodingHeaderValue> TransferEncoding
{
get { return GeneralHeaders.TransferEncoding; }
}
public bool? TransferEncodingChunked
{
get { return HttpGeneralHeaders.GetTransferEncodingChunked(this, _generalHeaders); } // special-cased to avoid forcing _generalHeaders initialization
set { GeneralHeaders.TransferEncodingChunked = value; }
}
public HttpHeaderValueCollection<ProductHeaderValue> Upgrade
{
get { return GeneralHeaders.Upgrade; }
}
public HttpHeaderValueCollection<ViaHeaderValue> Via
{
get { return GeneralHeaders.Via; }
}
public HttpHeaderValueCollection<WarningHeaderValue> Warning
{
get { return GeneralHeaders.Warning; }
}
#endregion
internal HttpResponseHeaders(bool containsTrailingHeaders = false)
: base(containsTrailingHeaders ? HttpHeaderType.All ^ HttpHeaderType.Request : HttpHeaderType.General | HttpHeaderType.Response | HttpHeaderType.Custom,
HttpHeaderType.Request)
{
_containsTrailingHeaders = containsTrailingHeaders;
}
internal bool ContainsTrailingHeaders => _containsTrailingHeaders;
internal override void AddHeaders(HttpHeaders sourceHeaders)
{
base.AddHeaders(sourceHeaders);
HttpResponseHeaders? sourceResponseHeaders = sourceHeaders as HttpResponseHeaders;
Debug.Assert(sourceResponseHeaders != null);
// Copy special values, but do not overwrite
if (sourceResponseHeaders._generalHeaders != null)
{
GeneralHeaders.AddSpecialsFrom(sourceResponseHeaders._generalHeaders);
}
}
internal override bool IsAllowedHeaderName(HeaderDescriptor descriptor)
{
if (!_containsTrailingHeaders)
return true;
KnownHeader? knownHeader = KnownHeaders.TryGetKnownHeader(descriptor.Name);
if (knownHeader == null)
return true;
return (knownHeader.HeaderType & HttpHeaderType.NonTrailing) == 0;
}
private HttpGeneralHeaders GeneralHeaders => _generalHeaders ??= new HttpGeneralHeaders(this);
}
}
|