| File: OutputCacheEntry.cs | Web Access |
| Project: src\aspnetcore\src\Middleware\OutputCaching\src\Microsoft.AspNetCore.OutputCaching.csproj (Microsoft.AspNetCore.OutputCaching) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; using System.IO.Pipelines; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Primitives; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.OutputCaching; internal sealed class OutputCacheEntry : IDisposable { public OutputCacheEntry(DateTimeOffset created, int statusCode) { Created = created; StatusCode = statusCode; } public StringValues FindHeader(string key) { TryFindHeader(key, out var value); return value; } public bool TryFindHeader(string key, out StringValues values) { foreach (var header in Headers.Span) { if (string.Equals(key, header.Name, StringComparison.OrdinalIgnoreCase)) { values = header.Value; return true; } } values = StringValues.Empty; return false; } /// <summary> /// Gets the created date and time of the cache entry. /// </summary> public DateTimeOffset Created { get; } /// <summary> /// Gets the status code of the cache entry. /// </summary> public int StatusCode { get; } /// <summary> /// Gets the headers of the cache entry. /// </summary> public ReadOnlyMemory<(string Name, StringValues Value)> Headers { get; private set; } // this is intentionally not an internal setter to make it clear that this should not be // used from most scenarios; this should consider buffer reuse - you *probably* want CopyFrom internal void SetHeaders(ReadOnlyMemory<(string Name, StringValues Value)> value) => Headers = value; /// <summary> /// Gets the body of the cache entry. /// </summary> public ReadOnlySequence<byte> Body { get; private set; } // this is intentionally not an internal setter to make it clear that this should not be // used from most scenarios; this should consider buffer reuse - you *probably* want CopyFrom internal void SetBody(ReadOnlySequence<byte> value, bool recycleBuffers) { Body = value; _ = recycleBuffers; // satisfy IDE0060 // note that recycleBuffers is not stored currently, until OutputCacheEntry buffer recycling is re-implemented; // it indicates whether this instance "owns" the memory behind the segments, such that they can be recycled later if desired } internal OutputCacheEntry CreateBodyFrom(IList<byte[]> segments) // mainly used from tests { // only expected in create path; don't reset/recycle existing Body = RecyclableReadOnlySequenceSegment.CreateSequence(segments); return this; } internal OutputCacheEntry CopyHeadersFrom(IHeaderDictionary headers) { // only expected in create path; don't reset/recycle existing if (headers is not null) { var count = headers.Count; var index = 0; if (count != 0) { var arr = ArrayPool<(string, StringValues)>.Shared.Rent(count); foreach (var header in headers) { if (OutputCacheEntryFormatter.ShouldStoreHeader(header.Key)) { arr[index++] = (header.Key, header.Value); } } if (index == 0) // only ignored headers { ArrayPool<(string, StringValues)>.Shared.Return(arr); Headers = default; } else { Headers = new(arr, 0, index); } } } return this; } public void CopyHeadersTo(IHeaderDictionary headers) { if (!TryFindHeader(HeaderNames.TransferEncoding, out _)) { headers.ContentLength = Body.Length; } foreach (var header in Headers.Span) { headers[header.Name] = header.Value; } } public ValueTask CopyToAsync(PipeWriter destination, CancellationToken cancellationToken) => RecyclableReadOnlySequenceSegment.CopyToAsync(Body, destination, cancellationToken); public void Dispose() { } // intention here is to add recycling; this was removed late in NET8, but is retained as a callback }