| | | 1 | | // Licensed to the .NET Foundation under one or more agreements. |
| | | 2 | | // The .NET Foundation licenses this file to you under the MIT license. |
| | | 3 | | |
| | | 4 | | using System.Diagnostics; |
| | | 5 | | |
| | | 6 | | namespace System.Net.Http.Headers |
| | | 7 | | { |
| | | 8 | | public sealed class HttpResponseHeaders : HttpHeaders |
| | | 9 | | { |
| | | 10 | | private const int AcceptRangesSlot = 0; |
| | | 11 | | private const int ProxyAuthenticateSlot = 1; |
| | | 12 | | private const int ServerSlot = 2; |
| | | 13 | | private const int VarySlot = 3; |
| | | 14 | | private const int WwwAuthenticateSlot = 4; |
| | | 15 | | private const int NumCollectionsSlots = 5; |
| | | 16 | | |
| | | 17 | | private object[]? _specialCollectionsSlots; |
| | | 18 | | private HttpGeneralHeaders? _generalHeaders; |
| | | 19 | | private readonly bool _containsTrailingHeaders; |
| | | 20 | | |
| | | 21 | | #region Response Headers |
| | | 22 | | |
| | | 23 | | private T GetSpecializedCollection<T>(int slot, Func<HttpResponseHeaders, T> creationFunc) |
| | 0 | 24 | | { |
| | | 25 | | // 5 properties each lazily allocate a collection to store the value(s) for that property. |
| | | 26 | | // Rather than having a field for each of these, store them untyped in an array that's lazily |
| | | 27 | | // allocated. Then we only pay for the 45 bytes for those fields when any is actually accessed. |
| | 0 | 28 | | object[] collections = _specialCollectionsSlots ??= new object[NumCollectionsSlots]; |
| | 0 | 29 | | return (T)(collections[slot] ??= creationFunc(this)!); |
| | 0 | 30 | | } |
| | | 31 | | |
| | | 32 | | public HttpHeaderValueCollection<string> AcceptRanges => |
| | 0 | 33 | | GetSpecializedCollection(AcceptRangesSlot, static thisRef => new HttpHeaderValueCollection<string>(KnownHead |
| | | 34 | | |
| | | 35 | | public TimeSpan? Age |
| | | 36 | | { |
| | 0 | 37 | | get { return HeaderUtilities.GetTimeSpanValue(KnownHeaders.Age.Descriptor, this); } |
| | 0 | 38 | | set { SetOrRemoveParsedValue(KnownHeaders.Age.Descriptor, value); } |
| | | 39 | | } |
| | | 40 | | |
| | | 41 | | public EntityTagHeaderValue? ETag |
| | | 42 | | { |
| | 0 | 43 | | get { return (EntityTagHeaderValue?)GetSingleParsedValue(KnownHeaders.ETag.Descriptor); } |
| | 0 | 44 | | set { SetOrRemoveParsedValue(KnownHeaders.ETag.Descriptor, value); } |
| | | 45 | | } |
| | | 46 | | |
| | | 47 | | public Uri? Location |
| | | 48 | | { |
| | 0 | 49 | | get { return (Uri?)GetSingleParsedValue(KnownHeaders.Location.Descriptor); } |
| | 0 | 50 | | set { SetOrRemoveParsedValue(KnownHeaders.Location.Descriptor, value); } |
| | | 51 | | } |
| | | 52 | | |
| | | 53 | | public HttpHeaderValueCollection<AuthenticationHeaderValue> ProxyAuthenticate => |
| | 0 | 54 | | GetSpecializedCollection(ProxyAuthenticateSlot, static thisRef => new HttpHeaderValueCollection<Authenticati |
| | | 55 | | |
| | | 56 | | public RetryConditionHeaderValue? RetryAfter |
| | | 57 | | { |
| | 0 | 58 | | get { return (RetryConditionHeaderValue?)GetSingleParsedValue(KnownHeaders.RetryAfter.Descriptor); } |
| | 0 | 59 | | set { SetOrRemoveParsedValue(KnownHeaders.RetryAfter.Descriptor, value); } |
| | | 60 | | } |
| | | 61 | | |
| | | 62 | | public HttpHeaderValueCollection<ProductInfoHeaderValue> Server => |
| | 0 | 63 | | GetSpecializedCollection(ServerSlot, static thisRef => new HttpHeaderValueCollection<ProductInfoHeaderValue> |
| | | 64 | | |
| | | 65 | | public HttpHeaderValueCollection<string> Vary => |
| | 0 | 66 | | GetSpecializedCollection(VarySlot, static thisRef => new HttpHeaderValueCollection<string>(KnownHeaders.Vary |
| | | 67 | | |
| | | 68 | | public HttpHeaderValueCollection<AuthenticationHeaderValue> WwwAuthenticate => |
| | 0 | 69 | | GetSpecializedCollection(WwwAuthenticateSlot, static thisRef => new HttpHeaderValueCollection<Authentication |
| | | 70 | | |
| | | 71 | | #endregion |
| | | 72 | | |
| | | 73 | | #region General Headers |
| | | 74 | | |
| | | 75 | | public CacheControlHeaderValue? CacheControl |
| | | 76 | | { |
| | 0 | 77 | | get { return GeneralHeaders.CacheControl; } |
| | 0 | 78 | | set { GeneralHeaders.CacheControl = value; } |
| | | 79 | | } |
| | | 80 | | |
| | | 81 | | public HttpHeaderValueCollection<string> Connection |
| | | 82 | | { |
| | 0 | 83 | | get { return GeneralHeaders.Connection; } |
| | | 84 | | } |
| | | 85 | | |
| | | 86 | | public bool? ConnectionClose |
| | | 87 | | { |
| | 0 | 88 | | get { return HttpGeneralHeaders.GetConnectionClose(this, _generalHeaders); } // special-cased to avoid forci |
| | 0 | 89 | | set { GeneralHeaders.ConnectionClose = value; } |
| | | 90 | | } |
| | | 91 | | |
| | | 92 | | public DateTimeOffset? Date |
| | | 93 | | { |
| | 0 | 94 | | get { return GeneralHeaders.Date; } |
| | 0 | 95 | | set { GeneralHeaders.Date = value; } |
| | | 96 | | } |
| | | 97 | | |
| | | 98 | | public HttpHeaderValueCollection<NameValueHeaderValue> Pragma |
| | | 99 | | { |
| | 0 | 100 | | get { return GeneralHeaders.Pragma; } |
| | | 101 | | } |
| | | 102 | | |
| | | 103 | | public HttpHeaderValueCollection<string> Trailer |
| | | 104 | | { |
| | 0 | 105 | | get { return GeneralHeaders.Trailer; } |
| | | 106 | | } |
| | | 107 | | |
| | | 108 | | public HttpHeaderValueCollection<TransferCodingHeaderValue> TransferEncoding |
| | | 109 | | { |
| | 0 | 110 | | get { return GeneralHeaders.TransferEncoding; } |
| | | 111 | | } |
| | | 112 | | |
| | | 113 | | public bool? TransferEncodingChunked |
| | | 114 | | { |
| | 0 | 115 | | get { return HttpGeneralHeaders.GetTransferEncodingChunked(this, _generalHeaders); } // special-cased to avo |
| | 0 | 116 | | set { GeneralHeaders.TransferEncodingChunked = value; } |
| | | 117 | | } |
| | | 118 | | |
| | | 119 | | public HttpHeaderValueCollection<ProductHeaderValue> Upgrade |
| | | 120 | | { |
| | 0 | 121 | | get { return GeneralHeaders.Upgrade; } |
| | | 122 | | } |
| | | 123 | | |
| | | 124 | | public HttpHeaderValueCollection<ViaHeaderValue> Via |
| | | 125 | | { |
| | 0 | 126 | | get { return GeneralHeaders.Via; } |
| | | 127 | | } |
| | | 128 | | |
| | | 129 | | public HttpHeaderValueCollection<WarningHeaderValue> Warning |
| | | 130 | | { |
| | 0 | 131 | | get { return GeneralHeaders.Warning; } |
| | | 132 | | } |
| | | 133 | | |
| | | 134 | | #endregion |
| | | 135 | | |
| | | 136 | | internal HttpResponseHeaders(bool containsTrailingHeaders = false) |
| | 1 | 137 | | : base(containsTrailingHeaders ? HttpHeaderType.All ^ HttpHeaderType.Request : HttpHeaderType.General | Http |
| | 1 | 138 | | HttpHeaderType.Request) |
| | 1 | 139 | | { |
| | 1 | 140 | | _containsTrailingHeaders = containsTrailingHeaders; |
| | 1 | 141 | | } |
| | | 142 | | |
| | 0 | 143 | | internal bool ContainsTrailingHeaders => _containsTrailingHeaders; |
| | | 144 | | |
| | | 145 | | internal override void AddHeaders(HttpHeaders sourceHeaders) |
| | 0 | 146 | | { |
| | 0 | 147 | | base.AddHeaders(sourceHeaders); |
| | 0 | 148 | | HttpResponseHeaders? sourceResponseHeaders = sourceHeaders as HttpResponseHeaders; |
| | 0 | 149 | | Debug.Assert(sourceResponseHeaders != null); |
| | | 150 | | |
| | | 151 | | // Copy special values, but do not overwrite |
| | 0 | 152 | | if (sourceResponseHeaders._generalHeaders != null) |
| | 0 | 153 | | { |
| | 0 | 154 | | GeneralHeaders.AddSpecialsFrom(sourceResponseHeaders._generalHeaders); |
| | 0 | 155 | | } |
| | 0 | 156 | | } |
| | | 157 | | |
| | | 158 | | internal override bool IsAllowedHeaderName(HeaderDescriptor descriptor) |
| | 19123 | 159 | | { |
| | 19123 | 160 | | if (!_containsTrailingHeaders) |
| | 19123 | 161 | | return true; |
| | | 162 | | |
| | 0 | 163 | | KnownHeader? knownHeader = KnownHeaders.TryGetKnownHeader(descriptor.Name); |
| | 0 | 164 | | if (knownHeader == null) |
| | 0 | 165 | | return true; |
| | | 166 | | |
| | 0 | 167 | | return (knownHeader.HeaderType & HttpHeaderType.NonTrailing) == 0; |
| | 19123 | 168 | | } |
| | | 169 | | |
| | 0 | 170 | | private HttpGeneralHeaders GeneralHeaders => _generalHeaders ??= new HttpGeneralHeaders(this); |
| | | 171 | | } |
| | | 172 | | } |