< Summary

Information
Class: System.Net.Http.HttpContent
Assembly: System.Net.Http
File(s): D:\runner\runtime\src\libraries\System.Net.Http\src\System\Net\Http\HttpContent.cs
Line coverage
4%
Covered lines: 27
Uncovered lines: 590
Coverable lines: 617
Total lines: 1158
Line coverage: 4.3%
Branch coverage
3%
Covered branches: 7
Total branches: 210
Branch coverage: 3.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%11100%
AssertEncodingConstants(...)50%88100%
SetHeaders(...)100%110%
.ctor()50%22100%
CreateMemoryStreamFromBufferedContent()100%110%
ReadAsStringAsync()100%110%
ReadAsStringAsync(...)100%110%
ReadBufferedContentAsString()100%110%
ReadBufferAsString(...)0%20200%
ReadAsByteArrayAsync()100%110%
ReadAsByteArrayAsync(...)100%110%
ReadBufferedContentAsByteArray()100%110%
ReadAsStream()100%110%
ReadAsStream(...)0%660%
ReadAsStreamAsync()100%110%
ReadAsStreamAsync(...)0%880%
TryReadAsStream()0%10100%
SerializeToStream(...)100%110%
SerializeToStreamAsync(...)100%110%
CopyTo(...)0%220%
CopyToAsync(...)100%110%
CopyToAsync(...)100%110%
CopyToAsync(...)100%110%
CopyToAsync(...)100%110%
WaitAsync()100%110%
InternalCopyToAsync(...)0%220%
LoadIntoBuffer(...)0%10100%
LoadIntoBufferAsync()100%110%
LoadIntoBufferAsync(...)100%110%
LoadIntoBufferAsync(...)100%110%
LoadIntoBufferAsync(...)0%660%
LoadIntoBufferAsyncCore()0%220%
CreateContentReadStream(...)100%110%
CreateContentReadStreamAsync()100%110%
CreateContentReadStreamAsync(...)100%110%
TryCreateContentReadStream()100%110%
GetComputedOrBufferLength()0%660%
CreateTemporaryBuffer(...)0%660%
Dispose(...)0%16160%
Dispose()100%110%
CheckDisposed()100%110%
CheckTaskNotNull(...)0%440%
StreamCopyExceptionNeedsWrapping(...)0%220%
GetStreamCopyException(...)0%220%
WrapStreamCopyException(...)0%220%
GetPreambleLength(...)0%22220%
TryDetectEncoding(...)0%880%
WaitAndReturnAsync()100%110%
CreateOverCapacityException(...)100%110%
.ctor(...)0%440%
Finalize()100%110%
Dispose(...)100%110%
ToArray()0%880%
GetSingleBuffer()100%110%
GetFirstBuffer()0%220%
CreateCopy()100%110%
ReallocateIfPooled()0%440%
Write(...)0%440%
GrowAndWrite(...)0%22220%
CopyToCore(...)0%880%
ReturnAllPooledBuffers()0%880%
Write(...)100%110%
WriteAsync(...)0%220%
WriteAsync(...)0%220%
BeginWrite(...)100%110%
EndWrite(...)100%110%
WriteByte(...)100%110%
Flush()100%110%
FlushAsync(...)100%110%
Read(...)100%110%
Seek(...)100%110%
SetLength(...)100%110%

File(s)

D:\runner\runtime\src\libraries\System.Net.Http\src\System\Net\Http\HttpContent.cs

#LineLine coverage
 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
 4using System.Buffers;
 5using System.Diagnostics;
 6using System.Diagnostics.CodeAnalysis;
 7using System.Globalization;
 8using System.IO;
 9using System.Net.Http.Headers;
 10using System.Runtime.ExceptionServices;
 11using System.Text;
 12using System.Threading;
 13using System.Threading.Tasks;
 14
 15namespace System.Net.Http
 16{
 17    public abstract class HttpContent : IDisposable
 18    {
 19        private HttpContentHeaders? _headers;
 20        private LimitArrayPoolWriteStream? _bufferedContent;
 21        private object? _contentReadStream; // Stream or Task<Stream>
 22        private bool _disposed;
 23        private bool _canCalculateLength;
 24
 25        internal const int MaxBufferSize = int.MaxValue;
 126        internal static readonly Encoding DefaultStringEncoding = Encoding.UTF8;
 27
 28        private const int UTF8CodePage = 65001;
 29        private const int UTF32CodePage = 12000;
 30        private const int UnicodeCodePage = 1200;
 31        private const int BigEndianUnicodeCodePage = 1201;
 32
 133        private static ReadOnlySpan<byte> UTF8Preamble => [0xEF, 0xBB, 0xBF];
 134        private static ReadOnlySpan<byte> UTF32Preamble => [0xFF, 0xFE, 0x00, 0x00];
 135        private static ReadOnlySpan<byte> UnicodePreamble => [0xFF, 0xFE];
 136        private static ReadOnlySpan<byte> BigEndianUnicodePreamble => [0xFE, 0xFF];
 37
 38#if DEBUG
 39        static HttpContent()
 140        {
 41            // Ensure the encoding constants used in this class match the actual data from the Encoding class
 142            AssertEncodingConstants(Encoding.UTF8, UTF8CodePage, UTF8Preamble);
 43
 44            // UTF32 not supported on Phone
 145            AssertEncodingConstants(Encoding.UTF32, UTF32CodePage, UTF32Preamble);
 46
 147            AssertEncodingConstants(Encoding.Unicode, UnicodeCodePage, UnicodePreamble);
 48
 149            AssertEncodingConstants(Encoding.BigEndianUnicode, BigEndianUnicodeCodePage, BigEndianUnicodePreamble);
 150        }
 51
 52        private static void AssertEncodingConstants(Encoding encoding, int codePage, ReadOnlySpan<byte> preamble)
 453        {
 454            Debug.Assert(encoding != null);
 55
 456            Debug.Assert(codePage == encoding.CodePage,
 457                $"Encoding code page mismatch for encoding: {encoding.EncodingName}",
 458                $"Expected (constant): {codePage}, Actual (Encoding.CodePage): {encoding.CodePage}");
 59
 460            byte[] actualPreamble = encoding.GetPreamble();
 61
 462            Debug.Assert(preamble.SequenceEqual(actualPreamble),
 463                $"Encoding preamble mismatch for encoding: {encoding.EncodingName}",
 464                $"Expected (constant): {BitConverter.ToString(preamble.ToArray())}, Actual (Encoding.GetPreamble()): {Bi
 465        }
 66#endif
 67
 168        public HttpContentHeaders Headers => _headers ??= new HttpContentHeaders(this);
 69
 70        internal void SetHeaders(HttpContentHeaders headers)
 071        {
 072            Debug.Assert(_headers is null);
 073            Debug.Assert(headers is not null);
 74
 075            headers._parent = this;
 076            _headers = headers;
 077        }
 78
 79        [MemberNotNullWhen(true, nameof(_bufferedContent))]
 080        private bool IsBuffered => _bufferedContent is not null;
 81
 182        protected HttpContent()
 183        {
 84            // Log to get an ID for the current content. This ID is used when the content gets associated to a message.
 185            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this);
 86
 87            // We start with the assumption that we can calculate the content length.
 188            _canCalculateLength = true;
 189        }
 90
 91        private MemoryStream CreateMemoryStreamFromBufferedContent()
 092        {
 093            Debug.Assert(IsBuffered);
 094            return new MemoryStream(_bufferedContent.GetSingleBuffer(), 0, (int)_bufferedContent.Length, writable: false
 095        }
 96
 97        public Task<string> ReadAsStringAsync() =>
 098            ReadAsStringAsync(CancellationToken.None);
 99
 100        public Task<string> ReadAsStringAsync(CancellationToken cancellationToken)
 0101        {
 0102            CheckDisposed();
 0103            return WaitAndReturnAsync(LoadIntoBufferAsync(cancellationToken), this, static s => s.ReadBufferedContentAsS
 0104        }
 105
 106        private string ReadBufferedContentAsString()
 0107        {
 0108            Debug.Assert(IsBuffered);
 109
 0110            return ReadBufferAsString(_bufferedContent, Headers);
 0111        }
 112
 113        internal static string ReadBufferAsString(LimitArrayPoolWriteStream stream, HttpContentHeaders headers)
 0114        {
 0115            if (stream.Length == 0)
 0116            {
 0117                return string.Empty;
 118            }
 119
 120            // We don't validate the Content-Encoding header: If the content was encoded, it's the caller's
 121            // responsibility to make sure to only call ReadAsString() on already decoded content. E.g. if the
 122            // Content-Encoding is 'gzip' the user should set HttpClientHandler.AutomaticDecompression to get a
 123            // decoded response stream.
 124
 0125            ReadOnlySpan<byte> firstBuffer = stream.GetFirstBuffer();
 0126            Debug.Assert(firstBuffer.Length >= 4 || firstBuffer.Length == stream.Length);
 127
 0128            Encoding? encoding = null;
 0129            int bomLength = -1;
 130
 0131            string? charset = headers.ContentType?.CharSet;
 132
 133            // If we do have encoding information in the 'Content-Type' header, use that information to convert
 134            // the content to a string.
 0135            if (charset != null)
 0136            {
 137                try
 0138                {
 139                    // Remove at most a single set of quotes.
 0140                    if (charset.Length > 2 &&
 0141                        charset.StartsWith('\"') &&
 0142                        charset.EndsWith('\"'))
 0143                    {
 0144                        encoding = Encoding.GetEncoding(charset.Substring(1, charset.Length - 2));
 0145                    }
 146                    else
 0147                    {
 0148                        encoding = Encoding.GetEncoding(charset);
 0149                    }
 150
 151                    // Byte-order-mark (BOM) characters may be present even if a charset was specified.
 0152                    bomLength = GetPreambleLength(firstBuffer, encoding);
 0153                }
 0154                catch (ArgumentException e)
 0155                {
 0156                    throw new InvalidOperationException(SR.net_http_content_invalid_charset, e);
 157                }
 0158            }
 159
 160            // If no content encoding is listed in the ContentType HTTP header, or no Content-Type header present,
 161            // then check for a BOM in the data to figure out the encoding.
 0162            if (encoding == null)
 0163            {
 0164                if (!TryDetectEncoding(firstBuffer, out encoding, out bomLength))
 0165                {
 166                    // Use the default encoding (UTF8) if we couldn't detect one.
 0167                    encoding = DefaultStringEncoding;
 168
 169                    // We already checked to see if the data had a UTF8 BOM in TryDetectEncoding
 170                    // and DefaultStringEncoding is UTF8, so the bomLength is 0.
 0171                    bomLength = 0;
 0172                }
 0173            }
 174
 175            // Drop the BOM when decoding the data.
 176
 0177            if (firstBuffer.Length == stream.Length)
 0178            {
 0179                return encoding.GetString(firstBuffer[bomLength..]);
 180            }
 181            else
 0182            {
 0183                byte[] buffer = ArrayPool<byte>.Shared.Rent((int)stream.Length);
 0184                stream.CopyToCore(buffer);
 185
 0186                string result = encoding.GetString(buffer.AsSpan(0, (int)stream.Length)[bomLength..]);
 187
 0188                ArrayPool<byte>.Shared.Return(buffer);
 0189                return result;
 190            }
 0191        }
 192
 193        public Task<byte[]> ReadAsByteArrayAsync() =>
 0194            ReadAsByteArrayAsync(CancellationToken.None);
 195
 196        public Task<byte[]> ReadAsByteArrayAsync(CancellationToken cancellationToken)
 0197        {
 0198            CheckDisposed();
 0199            return WaitAndReturnAsync(LoadIntoBufferAsync(cancellationToken), this, static s => s.ReadBufferedContentAsB
 0200        }
 201
 202        internal byte[] ReadBufferedContentAsByteArray()
 0203        {
 0204            Debug.Assert(_bufferedContent != null);
 205            // The returned array is exposed out of the library, so use CreateCopy rather than GetSingleBuffer.
 0206            return _bufferedContent.CreateCopy();
 0207        }
 208
 209        public Stream ReadAsStream() =>
 0210            ReadAsStream(CancellationToken.None);
 211
 212        public Stream ReadAsStream(CancellationToken cancellationToken)
 0213        {
 0214            CheckDisposed();
 215
 216            // _contentReadStream will be either null (nothing yet initialized), a Stream (it was previously
 217            // initialized in TryReadAsStream/ReadAsStream), or a Task<Stream> (it was previously initialized
 218            // in ReadAsStreamAsync).
 219
 0220            if (_contentReadStream == null) // don't yet have a Stream
 0221            {
 0222                Stream s = IsBuffered ?
 0223                    CreateMemoryStreamFromBufferedContent() :
 0224                    CreateContentReadStream(cancellationToken);
 0225                _contentReadStream = s;
 0226                return s;
 227            }
 0228            else if (_contentReadStream is Stream stream) // have a Stream
 0229            {
 0230                return stream;
 231            }
 232            else // have a Task<Stream>
 0233            {
 234                // Throw if ReadAsStreamAsync has been called previously since _contentReadStream contains a cached task
 0235                throw new HttpRequestException(SR.net_http_content_read_as_stream_has_task);
 236            }
 0237        }
 238
 239        public Task<Stream> ReadAsStreamAsync() =>
 0240            ReadAsStreamAsync(CancellationToken.None);
 241
 242        public Task<Stream> ReadAsStreamAsync(CancellationToken cancellationToken)
 0243        {
 0244            CheckDisposed();
 245
 246            // _contentReadStream will be either null (nothing yet initialized), a Stream (it was previously
 247            // initialized in TryReadAsStream/ReadAsStream), or a Task<Stream> (it was previously initialized here
 248            // in ReadAsStreamAsync).
 249
 0250            if (_contentReadStream == null) // don't yet have a Stream
 0251            {
 0252                Task<Stream> t = IsBuffered ?
 0253                    Task.FromResult<Stream>(CreateMemoryStreamFromBufferedContent()) :
 0254                    CreateContentReadStreamAsync(cancellationToken);
 0255                _contentReadStream = t;
 0256                return t;
 257            }
 0258            else if (_contentReadStream is Task<Stream> t) // have a Task<Stream>
 0259            {
 0260                return t;
 261            }
 262            else
 0263            {
 0264                Debug.Assert(_contentReadStream is Stream, $"Expected a Stream, got ${_contentReadStream}");
 0265                Task<Stream> ts = Task.FromResult((Stream)_contentReadStream);
 0266                _contentReadStream = ts;
 0267                return ts;
 268            }
 0269        }
 270
 271        internal Stream? TryReadAsStream()
 0272        {
 0273            CheckDisposed();
 274
 275            // _contentReadStream will be either null (nothing yet initialized), a Stream (it was previously
 276            // initialized in TryReadAsStream/ReadAsStream), or a Task<Stream> (it was previously initialized here
 277            // in ReadAsStreamAsync).
 278
 0279            if (_contentReadStream == null) // don't yet have a Stream
 0280            {
 0281                Stream? s = IsBuffered ?
 0282                    CreateMemoryStreamFromBufferedContent() :
 0283                    TryCreateContentReadStream();
 0284                _contentReadStream = s;
 0285                return s;
 286            }
 0287            else if (_contentReadStream is Stream s) // have a Stream
 0288            {
 0289                return s;
 290            }
 291            else // have a Task<Stream>
 0292            {
 0293                Debug.Assert(_contentReadStream is Task<Stream>, $"Expected a Task<Stream>, got ${_contentReadStream}");
 0294                Task<Stream> t = (Task<Stream>)_contentReadStream;
 0295                return t.Status == TaskStatus.RanToCompletion ? t.Result : null;
 296            }
 0297        }
 298
 299        protected abstract Task SerializeToStreamAsync(Stream stream, TransportContext? context);
 300
 301        // We cannot add abstract member to a public class in order to not to break already established contract of this
 302        // So we add virtual method, override it everywhere internally and provide proper implementation.
 303        // Unfortunately we cannot force everyone to implement so in such case we throw NSE.
 304        protected virtual void SerializeToStream(Stream stream, TransportContext? context, CancellationToken cancellatio
 0305        {
 0306            throw new NotSupportedException(SR.Format(SR.net_http_missing_sync_implementation, GetType(), nameof(HttpCon
 307        }
 308
 309        protected virtual Task SerializeToStreamAsync(Stream stream, TransportContext? context, CancellationToken cancel
 0310            SerializeToStreamAsync(stream, context);
 311
 312        // TODO https://github.com/dotnet/runtime/issues/31316: Expose something to enable this publicly.  For very spec
 313        // HTTP/2 scenarios (e.g. gRPC), we need to be able to allow request content to continue sending after SendAsync
 314        // completed, which goes against the previous design of content, and which means that with some servers, even ou
 315        // of desired scenarios we could end up unexpectedly having request content still sending even after the respons
 316        // completes, which could lead to spurious failures in unsuspecting client code.  To mitigate that, we prohibit 
 317        // on all known HttpContent types, waiting for the request content to complete before completing the SendAsync t
 0318        internal virtual bool AllowDuplex => true;
 319
 320        public void CopyTo(Stream stream, TransportContext? context, CancellationToken cancellationToken)
 0321        {
 0322            CheckDisposed();
 0323            ArgumentNullException.ThrowIfNull(stream);
 324            try
 0325            {
 0326                if (IsBuffered)
 0327                {
 0328                    stream.Write(_bufferedContent.GetSingleBuffer(), 0, (int)_bufferedContent.Length);
 0329                }
 330                else
 0331                {
 0332                    SerializeToStream(stream, context, cancellationToken);
 0333                }
 0334            }
 0335            catch (Exception e) when (StreamCopyExceptionNeedsWrapping(e))
 0336            {
 0337                throw GetStreamCopyException(e);
 338            }
 0339        }
 340
 341        public Task CopyToAsync(Stream stream) =>
 0342            CopyToAsync(stream, CancellationToken.None);
 343
 344        public Task CopyToAsync(Stream stream, CancellationToken cancellationToken) =>
 0345            CopyToAsync(stream, null, cancellationToken);
 346
 347        public Task CopyToAsync(Stream stream, TransportContext? context) =>
 0348            CopyToAsync(stream, context, CancellationToken.None);
 349
 350        public Task CopyToAsync(Stream stream, TransportContext? context, CancellationToken cancellationToken)
 0351        {
 0352            CheckDisposed();
 0353            ArgumentNullException.ThrowIfNull(stream);
 354            try
 0355            {
 0356                return WaitAsync(InternalCopyToAsync(stream, context, cancellationToken));
 357            }
 0358            catch (Exception e) when (StreamCopyExceptionNeedsWrapping(e))
 0359            {
 0360                return Task.FromException(GetStreamCopyException(e));
 361            }
 362
 363            static async Task WaitAsync(ValueTask copyTask)
 0364            {
 365                try
 0366                {
 0367                    await copyTask.ConfigureAwait(false);
 0368                }
 0369                catch (Exception e) when (StreamCopyExceptionNeedsWrapping(e))
 0370                {
 0371                    throw WrapStreamCopyException(e);
 372                }
 0373            }
 0374        }
 375
 376        internal ValueTask InternalCopyToAsync(Stream stream, TransportContext? context, CancellationToken cancellationT
 0377        {
 0378            if (IsBuffered)
 0379            {
 0380                return stream.WriteAsync(_bufferedContent.GetSingleBuffer().AsMemory(0, (int)_bufferedContent.Length), c
 381            }
 382
 0383            Task task = SerializeToStreamAsync(stream, context, cancellationToken);
 0384            CheckTaskNotNull(task);
 0385            return new ValueTask(task);
 0386        }
 387
 388        internal void LoadIntoBuffer(long maxBufferSize, CancellationToken cancellationToken)
 0389        {
 0390            CheckDisposed();
 391
 0392            if (!CreateTemporaryBuffer(maxBufferSize, out LimitArrayPoolWriteStream? tempBuffer, out Exception? error))
 0393            {
 394                // If we already buffered the content, just return.
 0395                return;
 396            }
 397
 0398            if (tempBuffer == null)
 0399            {
 0400                throw error!;
 401            }
 402
 403            // Register for cancellation and tear down the underlying stream in case of cancellation/timeout.
 404            // We're only comfortable disposing of the HttpContent instance like this because LoadIntoBuffer is internal
 405            // we're only using it on content instances we get back from a handler's Send call that haven't been given o
 406            // If we were to ever make LoadIntoBuffer public, we'd need to rethink this.
 0407            CancellationTokenRegistration cancellationRegistration = cancellationToken.Register(static s => ((HttpConten
 408
 409            try
 0410            {
 0411                SerializeToStream(tempBuffer, null, cancellationToken);
 0412            }
 0413            catch (Exception e)
 0414            {
 0415                tempBuffer.Dispose();
 416
 0417                if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, e);
 418
 0419                if (CancellationHelper.ShouldWrapInOperationCanceledException(e, cancellationToken))
 0420                {
 0421                    throw CancellationHelper.CreateOperationCanceledException(e, cancellationToken);
 422                }
 423
 0424                if (StreamCopyExceptionNeedsWrapping(e))
 0425                {
 0426                    throw GetStreamCopyException(e);
 427                }
 428
 0429                throw;
 430            }
 431            finally
 0432            {
 433                // Clean up the cancellation registration.
 0434                cancellationRegistration.Dispose();
 0435            }
 436
 0437            tempBuffer.ReallocateIfPooled();
 0438            _bufferedContent = tempBuffer;
 0439        }
 440
 441        public Task LoadIntoBufferAsync() =>
 0442            LoadIntoBufferAsync(MaxBufferSize);
 443
 444        // No "CancellationToken" parameter needed since canceling the CTS will close the connection, resulting
 445        // in an exception being thrown while we're buffering.
 446        // If buffering is used without a connection, it is supposed to be fast, thus no cancellation required.
 447        public Task LoadIntoBufferAsync(long maxBufferSize) =>
 0448            LoadIntoBufferAsync(maxBufferSize, CancellationToken.None);
 449
 450        /// <summary>
 451        /// Serialize the HTTP content to a memory buffer as an asynchronous operation.
 452        /// </summary>
 453        /// <param name="cancellationToken">The cancellation token to cancel the operation.</param>
 454        /// <returns>The task object representing the asynchronous operation.</returns>
 455        /// <remarks>
 456        /// This operation will not block. The returned <see cref="Task"/> object will complete after all of the content
 457        /// After content is serialized to a memory buffer, calls to one of the <see cref="CopyToAsync(Stream)"/> method
 458        /// </remarks>
 459        /// <exception cref="OperationCanceledException">The cancellation token was canceled. This exception is stored i
 460        /// <exception cref="ObjectDisposedException">The object has already been disposed.</exception>
 461        public Task LoadIntoBufferAsync(CancellationToken cancellationToken) =>
 0462            LoadIntoBufferAsync(MaxBufferSize, cancellationToken);
 463
 464        /// <summary>
 465        /// Serialize the HTTP content to a memory buffer as an asynchronous operation.
 466        /// </summary>
 467        /// <param name="maxBufferSize">The maximum size, in bytes, of the buffer to use.</param>
 468        /// <param name="cancellationToken">The cancellation token to cancel the operation.</param>
 469        /// <returns>The task object representing the asynchronous operation.</returns>
 470        /// <remarks>
 471        /// This operation will not block. The returned <see cref="Task"/> object will complete after all of the content
 472        /// After content is serialized to a memory buffer, calls to one of the <see cref="CopyToAsync(Stream)"/> method
 473        /// </remarks>
 474        /// <exception cref="OperationCanceledException">The cancellation token was canceled. This exception is stored i
 475        /// <exception cref="ObjectDisposedException">The object has already been disposed.</exception>
 476        public Task LoadIntoBufferAsync(long maxBufferSize, CancellationToken cancellationToken)
 0477        {
 0478            CheckDisposed();
 479
 0480            if (!CreateTemporaryBuffer(maxBufferSize, out LimitArrayPoolWriteStream? tempBuffer, out Exception? error))
 0481            {
 482                // If we already buffered the content, just return a completed task.
 0483                return Task.CompletedTask;
 484            }
 485
 0486            if (tempBuffer == null)
 0487            {
 488                // We don't throw in LoadIntoBufferAsync(): return a faulted task.
 0489                return Task.FromException(error!);
 490            }
 491
 492            try
 0493            {
 494#pragma warning disable CA2025
 0495                Task task = SerializeToStreamAsync(tempBuffer, null, cancellationToken);
 0496                CheckTaskNotNull(task);
 0497                return LoadIntoBufferAsyncCore(task, tempBuffer);
 498#pragma warning restore
 499            }
 0500            catch (Exception e)
 0501            {
 0502                tempBuffer.Dispose();
 503
 0504                if (StreamCopyExceptionNeedsWrapping(e))
 0505                {
 0506                    return Task.FromException(GetStreamCopyException(e));
 507                }
 508
 509                // other synchronous exceptions from SerializeToStreamAsync/CheckTaskNotNull will propagate
 0510                throw;
 511            }
 0512        }
 513
 514        private async Task LoadIntoBufferAsyncCore(Task serializeToStreamTask, LimitArrayPoolWriteStream tempBuffer)
 0515        {
 516            try
 0517            {
 0518                await serializeToStreamTask.ConfigureAwait(false);
 0519            }
 0520            catch (Exception e)
 0521            {
 0522                tempBuffer.Dispose(); // Cleanup partially filled stream.
 0523                Exception we = GetStreamCopyException(e);
 0524                if (we != e) throw we;
 0525                throw;
 526            }
 527
 0528            tempBuffer.ReallocateIfPooled();
 0529            _bufferedContent = tempBuffer;
 0530        }
 531
 532        /// <summary>
 533        /// Serializes the HTTP content to a memory stream.
 534        /// </summary>
 535        /// <param name="cancellationToken">The cancellation token to cancel the operation.</param>
 536        /// <returns>The output memory stream which contains the serialized HTTP content.</returns>
 537        /// <remarks>
 538        /// Once the operation completes, the returned memory stream represents the HTTP content. The returned stream ca
 539        /// The <see cref="CreateContentReadStream(CancellationToken)"/> method buffers the content to a memory stream.
 540        /// Derived classes can override this behavior if there is a better way to retrieve the content as stream.
 541        /// For example, a byte array or a string could use a more efficient method way such as wrapping a read-only Mem
 542        /// </remarks>
 543        protected virtual Stream CreateContentReadStream(CancellationToken cancellationToken)
 0544        {
 0545            LoadIntoBuffer(MaxBufferSize, cancellationToken);
 0546            return CreateMemoryStreamFromBufferedContent();
 0547        }
 548
 549        protected virtual Task<Stream> CreateContentReadStreamAsync()
 0550        {
 551            // By default just buffer the content to a memory stream. Derived classes can override this behavior
 552            // if there is a better way to retrieve the content as stream (e.g. byte array/string use a more efficient
 553            // way, like wrapping a read-only MemoryStream around the bytes/string)
 0554            return WaitAndReturnAsync(LoadIntoBufferAsync(), this, static s => (Stream)s.CreateMemoryStreamFromBufferedC
 0555        }
 556
 557        protected virtual Task<Stream> CreateContentReadStreamAsync(CancellationToken cancellationToken)
 0558        {
 559            // Drops the CT for compatibility reasons, see https://github.com/dotnet/runtime/issues/916#issuecomment-562
 0560            return CreateContentReadStreamAsync();
 0561        }
 562
 563        // As an optimization for internal consumers of HttpContent (e.g. HttpClient.GetStreamAsync), and for
 564        // HttpContent-derived implementations that override CreateContentReadStreamAsync in a way that always
 565        // or frequently returns synchronously-completed tasks, we can avoid the task allocation by enabling
 566        // callers to try to get the Stream first synchronously.
 0567        internal virtual Stream? TryCreateContentReadStream() => null;
 568
 569        // Derived types return true if they're able to compute the length. It's OK if derived types return false to
 570        // indicate that they're not able to compute the length. The transport channel needs to decide what to do in
 571        // that case (send chunked, buffer first, etc.).
 572        protected internal abstract bool TryComputeLength(out long length);
 573
 574        internal long? GetComputedOrBufferLength()
 0575        {
 0576            CheckDisposed();
 577
 0578            if (IsBuffered)
 0579            {
 0580                return _bufferedContent.Length;
 581            }
 582
 583            // If we already tried to calculate the length, but the derived class returned 'false', then don't try
 584            // again; just return null.
 0585            if (_canCalculateLength)
 0586            {
 587                long length;
 0588                if (TryComputeLength(out length))
 0589                {
 0590                    return length;
 591                }
 592
 593                // Set flag to make sure next time we don't try to compute the length, since we know that we're unable
 594                // to do so.
 0595                _canCalculateLength = false;
 0596            }
 0597            return null;
 0598        }
 599
 600        private bool CreateTemporaryBuffer(long maxBufferSize, out LimitArrayPoolWriteStream? tempBuffer, out Exception?
 0601        {
 0602            if (maxBufferSize > HttpContent.MaxBufferSize)
 0603            {
 604                // This should only be hit when called directly; HttpClient/HttpClientHandler
 605                // will not exceed this limit.
 0606                throw new ArgumentOutOfRangeException(nameof(maxBufferSize), maxBufferSize,
 0607                    SR.Format(CultureInfo.InvariantCulture,
 0608                        SR.net_http_content_buffersize_limit, HttpContent.MaxBufferSize));
 609            }
 610
 0611            if (IsBuffered)
 0612            {
 613                // If we already buffered the content, just return false.
 0614                tempBuffer = default;
 0615                error = default;
 0616                return false;
 617            }
 618
 619            // If we have a Content-Length allocate the right amount of buffer up-front. Also check whether the
 620            // content length exceeds the max. buffer size.
 0621            long contentLength = Headers.ContentLength.GetValueOrDefault();
 0622            Debug.Assert(contentLength >= 0);
 623
 0624            if (contentLength > maxBufferSize)
 0625            {
 0626                tempBuffer = null;
 0627                error = CreateOverCapacityException(maxBufferSize);
 0628            }
 629            else
 0630            {
 0631                tempBuffer = new LimitArrayPoolWriteStream((int)maxBufferSize, contentLength, getFinalSizeFromPool: fals
 0632                error = null;
 0633            }
 634
 0635            return true;
 0636        }
 637
 638        #region IDisposable Members
 639
 640        protected virtual void Dispose(bool disposing)
 0641        {
 0642            if (disposing && !_disposed)
 0643            {
 0644                _disposed = true;
 645
 0646                if (_contentReadStream != null)
 0647                {
 0648                    Stream? s = _contentReadStream as Stream ??
 0649                        (_contentReadStream is Task<Stream> t && t.Status == TaskStatus.RanToCompletion ? t.Result : nul
 0650                    s?.Dispose();
 0651                    _contentReadStream = null;
 0652                }
 653
 0654                if (IsBuffered)
 0655                {
 0656                    _bufferedContent.Dispose();
 0657                }
 0658            }
 0659        }
 660
 661        public void Dispose()
 0662        {
 0663            Dispose(true);
 0664            GC.SuppressFinalize(this);
 0665        }
 666
 667        #endregion
 668
 669        #region Helpers
 670
 671        private void CheckDisposed()
 0672        {
 0673            ObjectDisposedException.ThrowIf(_disposed, this);
 0674        }
 675
 676        private void CheckTaskNotNull(Task task)
 0677        {
 0678            if (task == null)
 0679            {
 0680                var e = new InvalidOperationException(SR.net_http_content_no_task_returned);
 0681                if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, e);
 0682                throw e;
 683            }
 0684        }
 685
 0686        internal static bool StreamCopyExceptionNeedsWrapping(Exception e) => e is IOException || e is ObjectDisposedExc
 687
 688        private static Exception GetStreamCopyException(Exception originalException)
 0689        {
 690            // HttpContent derived types should throw HttpRequestExceptions if there is an error. However, since the str
 691            // provided by CopyToAsync() can also throw, we wrap such exceptions in HttpRequestException. This way custo
 692            // types don't have to worry about it. The goal is that users of HttpContent don't have to catch multiple
 693            // exceptions (depending on the underlying transport), but just HttpRequestExceptions
 694            // Custom stream should throw either IOException or HttpRequestException.
 695            // We don't want to wrap other exceptions thrown by Stream (e.g. InvalidOperationException), since we
 696            // don't want to hide such "usage error" exceptions in HttpRequestException.
 697            // ObjectDisposedException is also wrapped, since aborting HWR after a request is complete will result in
 698            // the response stream being closed.
 0699            return StreamCopyExceptionNeedsWrapping(originalException) ?
 0700                WrapStreamCopyException(originalException) :
 0701                originalException;
 0702        }
 703
 704        internal static Exception WrapStreamCopyException(Exception e)
 0705        {
 0706            Debug.Assert(StreamCopyExceptionNeedsWrapping(e));
 0707            HttpRequestError error = e is HttpIOException ioEx ? ioEx.HttpRequestError : HttpRequestError.Unknown;
 0708            return ExceptionDispatchInfo.SetCurrentStackTrace(new HttpRequestException(error, SR.net_http_content_stream
 0709        }
 710
 711        private static int GetPreambleLength(ReadOnlySpan<byte> data, Encoding encoding)
 0712        {
 0713            Debug.Assert(encoding != null);
 714
 0715            switch (encoding.CodePage)
 716            {
 717                case UTF8CodePage:
 0718                    return data.StartsWith(UTF8Preamble) ? UTF8Preamble.Length : 0;
 719
 720                case UTF32CodePage:
 0721                    return data.StartsWith(UTF32Preamble) ? UTF32Preamble.Length : 0;
 722
 723                case UnicodeCodePage:
 0724                    return data.StartsWith(UnicodePreamble) ? UnicodePreamble.Length : 0;
 725
 726                case BigEndianUnicodeCodePage:
 0727                    return data.StartsWith(BigEndianUnicodePreamble) ? BigEndianUnicodePreamble.Length : 0;
 728
 729                default:
 0730                    byte[] preamble = encoding.GetPreamble();
 0731                    return preamble is not null && data.StartsWith(preamble) ? preamble.Length : 0;
 732            }
 0733        }
 734
 735        private static bool TryDetectEncoding(ReadOnlySpan<byte> data, [NotNullWhen(true)] out Encoding? encoding, out i
 0736        {
 0737            if (data.StartsWith(UTF8Preamble))
 0738            {
 0739                encoding = Encoding.UTF8;
 0740                preambleLength = UTF8Preamble.Length;
 0741                return true;
 742            }
 743
 0744            if (data.StartsWith(UTF32Preamble))
 0745            {
 0746                encoding = Encoding.UTF32;
 0747                preambleLength = UTF32Preamble.Length;
 0748                return true;
 749            }
 750
 0751            if (data.StartsWith(UnicodePreamble))
 0752            {
 0753                encoding = Encoding.Unicode;
 0754                preambleLength = UnicodePreamble.Length;
 0755                return true;
 756            }
 757
 0758            if (data.StartsWith(BigEndianUnicodePreamble))
 0759            {
 0760                encoding = Encoding.BigEndianUnicode;
 0761                preambleLength = BigEndianUnicodePreamble.Length;
 0762                return true;
 763            }
 764
 0765            encoding = null;
 0766            preambleLength = 0;
 0767            return false;
 0768        }
 769
 770        #endregion Helpers
 771
 772        private static async Task<TResult> WaitAndReturnAsync<TState, TResult>(Task waitTask, TState state, Func<TState,
 0773        {
 0774            await waitTask.ConfigureAwait(false);
 0775            return returnFunc(state);
 0776        }
 777
 778        private static HttpRequestException CreateOverCapacityException(long maxBufferSize)
 0779        {
 0780            return (HttpRequestException)ExceptionDispatchInfo.SetCurrentStackTrace(new HttpRequestException(HttpRequest
 0781        }
 782
 783        /// <summary>
 784        /// A write-only stream that limits the total length of the content to <see cref="_maxBufferSize"/>.
 785        /// It uses pooled buffers for the content, but can switch to a regular array allocation, which is useful when t
 786        /// already knows the final size and needs a new array anyway (e.g. <see cref="HttpClient.GetByteArrayAsync(stri
 787        /// <para>Since we can't rely on users to reliably dispose content objects, any pooled buffers must be returned 
 788        /// the execution path we control. In practice this means <see cref="LoadIntoBufferAsync()"/> must call <see cre
 789        /// before storing the stream in <see cref="_bufferedContent"/>.</para>
 790        /// </summary>
 791        internal sealed class LimitArrayPoolWriteStream : Stream
 792        {
 793            /// <summary>Applies when a Content-Length header was not specified.</summary>
 794            private const int MinInitialBufferSize = 16 * 1024; // 16 KB
 795
 796            /// <summary>Applies when a Content-Length header was set. If it's &lt;= this limit, we'll allocate an exact
 797            private const int MaxInitialBufferSize = 16 * 1024 * 1024; // 16 MB
 798
 799            private const int ResizeFactor = 2;
 800
 801            /// <summary>Controls how quickly we're willing to expand up to <see cref="_expectedFinalSize"/> when a call
 802            /// <para>The factor is higher than usual to lower the number of memory copies when the caller already commi
 803            private const int LastResizeFactor = 4;
 804
 805            /// <summary><see cref="_totalLength"/> may not exceed this limit, or we'll throw a <see cref="CreateOverCap
 806            private readonly int _maxBufferSize;
 807            /// <summary>The value of the Content-Length header or 0. <see cref="_totalLength"/> may exceed this value i
 808            private readonly int _expectedFinalSize;
 809            /// <summary>Indicates whether the caller will need an exactly-sized, non-pooled, buffer. The implementation
 810            private readonly bool _shouldPoolFinalSize;
 811
 812            private bool _lastBufferIsPooled;
 813            private byte[] _lastBuffer;
 814            private byte[]?[]? _pooledBuffers;
 815            private int _lastBufferOffset;
 816            private int _totalLength;
 817
 0818            public LimitArrayPoolWriteStream(int maxBufferSize, long expectedFinalSize, bool getFinalSizeFromPool)
 0819            {
 0820                Debug.Assert(maxBufferSize >= 0);
 0821                Debug.Assert(expectedFinalSize >= 0);
 822
 0823                if (expectedFinalSize > maxBufferSize)
 0824                {
 0825                    throw CreateOverCapacityException(maxBufferSize);
 826                }
 827
 0828                _maxBufferSize = maxBufferSize;
 0829                _expectedFinalSize = (int)expectedFinalSize;
 0830                _shouldPoolFinalSize = getFinalSizeFromPool || expectedFinalSize == 0;
 0831                _lastBufferIsPooled = false;
 0832                _lastBuffer = [];
 0833            }
 834
 835#if DEBUG
 836            ~LimitArrayPoolWriteStream()
 0837            {
 838                // Ensure that we're not leaking pooled buffers.
 0839                Debug.Assert(_pooledBuffers is null);
 0840                Debug.Assert(!_lastBufferIsPooled);
 0841            }
 842#endif
 843
 844            protected override void Dispose(bool disposing)
 0845            {
 0846                ReturnAllPooledBuffers();
 0847                base.Dispose(disposing);
 0848            }
 849
 850            /// <summary>Should only be called once.</summary>
 851            public byte[] ToArray()
 0852            {
 0853                Debug.Assert(!_shouldPoolFinalSize || _expectedFinalSize == 0);
 854
 0855                if (!_lastBufferIsPooled && _totalLength == _lastBuffer.Length)
 0856                {
 0857                    Debug.Assert(_pooledBuffers is null);
 0858                    return _lastBuffer;
 859                }
 860
 0861                if (_totalLength == 0)
 0862                {
 0863                    return [];
 864                }
 865
 0866                byte[] buffer = new byte[_totalLength];
 0867                CopyToCore(buffer);
 0868                return buffer;
 0869            }
 870
 871            /// <summary>Should only be called if <see cref="ReallocateIfPooled"/> was used to avoid exposing pooled buf
 872            public byte[] GetSingleBuffer()
 0873            {
 0874                Debug.Assert(!_lastBufferIsPooled);
 0875                Debug.Assert(_pooledBuffers is null);
 876
 0877                return _lastBuffer;
 0878            }
 879
 880            public ReadOnlySpan<byte> GetFirstBuffer()
 0881            {
 0882                return _pooledBuffers is byte[]?[] buffers
 0883                    ? buffers[0]
 0884                    : _lastBuffer.AsSpan(0, _totalLength);
 0885            }
 886
 887            public byte[] CreateCopy()
 0888            {
 0889                Debug.Assert(!_lastBufferIsPooled);
 0890                Debug.Assert(_pooledBuffers is null);
 0891                Debug.Assert(_lastBufferOffset == _totalLength);
 0892                Debug.Assert(_lastBufferOffset <= _lastBuffer.Length);
 893
 0894                return _lastBuffer.AsSpan(0, _totalLength).ToArray();
 0895            }
 896
 897            public void ReallocateIfPooled()
 0898            {
 0899                Debug.Assert(_lastBufferIsPooled || _pooledBuffers is null);
 900
 0901                if (_lastBufferIsPooled)
 0902                {
 0903                    byte[] newBuffer = new byte[_totalLength];
 0904                    CopyToCore(newBuffer);
 0905                    ReturnAllPooledBuffers();
 0906                    _lastBuffer = newBuffer;
 0907                    _lastBufferOffset = newBuffer.Length;
 0908                }
 0909            }
 910
 911            public override void Write(ReadOnlySpan<byte> buffer)
 0912            {
 0913                if (_maxBufferSize - _totalLength < buffer.Length)
 0914                {
 0915                    throw CreateOverCapacityException(_maxBufferSize);
 916                }
 917
 0918                byte[] lastBuffer = _lastBuffer;
 0919                int offset = _lastBufferOffset;
 920
 0921                if (lastBuffer.Length - offset >= buffer.Length)
 0922                {
 0923                    buffer.CopyTo(lastBuffer.AsSpan(offset));
 0924                    _lastBufferOffset = offset + buffer.Length;
 0925                    _totalLength += buffer.Length;
 0926                }
 927                else
 0928                {
 0929                    GrowAndWrite(buffer);
 0930                }
 0931            }
 932
 933            private void GrowAndWrite(ReadOnlySpan<byte> buffer)
 0934            {
 0935                Debug.Assert(_totalLength + buffer.Length <= _maxBufferSize);
 936
 0937                int lastBufferCapacity = _lastBuffer.Length;
 938
 939                // Start by doubling the current array size.
 0940                int newBufferCapacity = (int)Math.Min((uint)lastBufferCapacity * ResizeFactor, Array.MaxLength);
 941
 942                // If the required length is longer than Array.MaxLength, we'll let the runtime throw.
 0943                newBufferCapacity = Math.Max(newBufferCapacity, _totalLength + buffer.Length);
 944
 945                // If this is the first write, set an initial minimum size.
 0946                if (lastBufferCapacity == 0)
 0947                {
 0948                    int minCapacity = _expectedFinalSize == 0
 0949                        ? MinInitialBufferSize
 0950                        : Math.Min(_expectedFinalSize, MaxInitialBufferSize / LastResizeFactor);
 951
 0952                    newBufferCapacity = Math.Max(newBufferCapacity, minCapacity);
 0953                }
 954
 955                // Avoid having the last buffer expand beyond the size limit too much.
 956                // It may still go beyond the limit somewhat due to the ArrayPool's buffer sizes being powers of 2.
 0957                int currentTotalCapacity = _totalLength - _lastBufferOffset + lastBufferCapacity;
 0958                int remainingUntilMaxCapacity = _maxBufferSize - currentTotalCapacity;
 0959                newBufferCapacity = Math.Min(newBufferCapacity, remainingUntilMaxCapacity);
 960
 0961                int newTotalCapacity = currentTotalCapacity + newBufferCapacity;
 962
 0963                Debug.Assert(newBufferCapacity > 0);
 964                byte[] newBuffer;
 965
 966                // If we don't want to pool our last buffer and we're getting close to its size, allocate the exact leng
 0967                if (!_shouldPoolFinalSize && newTotalCapacity >= _expectedFinalSize / LastResizeFactor)
 0968                {
 969                    // We knew the Content-Length upfront, and the caller needs an exactly-sized, non-pooled, buffer.
 970                    // It's almost certain that the final length will match the expected size,
 971                    // so we switch from pooled buffers to a regular array now to reduce memory copies.
 972
 973                    // It's possible we're writing more bytes than the expected final size if the handler/content is not
 974                    // enforcing Content-Length correctness. Such requests will likely throw later on anyway.
 0975                    newBuffer = new byte[_totalLength + buffer.Length <= _expectedFinalSize ? _expectedFinalSize : newTo
 976
 0977                    CopyToCore(newBuffer);
 978
 0979                    ReturnAllPooledBuffers();
 980
 0981                    buffer.CopyTo(newBuffer.AsSpan(_totalLength));
 982
 0983                    _totalLength += buffer.Length;
 0984                    _lastBufferOffset = _totalLength;
 0985                    _lastBufferIsPooled = false;
 0986                }
 0987                else if (lastBufferCapacity == 0)
 0988                {
 989                    // This is the first write call, allocate the initial buffer.
 0990                    Debug.Assert(_pooledBuffers is null);
 0991                    Debug.Assert(_lastBufferOffset == 0);
 0992                    Debug.Assert(_totalLength == 0);
 993
 0994                    newBuffer = ArrayPool<byte>.Shared.Rent(newBufferCapacity);
 0995                    Debug.Assert(_shouldPoolFinalSize || newBuffer.Length != _expectedFinalSize);
 996
 0997                    buffer.CopyTo(newBuffer);
 0998                    _totalLength = _lastBufferOffset = buffer.Length;
 0999                    _lastBufferIsPooled = true;
 01000                }
 1001                else
 01002                {
 01003                    Debug.Assert(_lastBufferIsPooled);
 1004
 01005                    _totalLength += buffer.Length;
 1006
 1007                    // When buffers are stored in '_pooledBuffers', they are assumed to be full.
 1008                    // Copy as many bytes as we can fit into the current buffer now.
 01009                    Span<byte> remainingInCurrentBuffer = _lastBuffer.AsSpan(_lastBufferOffset);
 01010                    Debug.Assert(remainingInCurrentBuffer.Length < buffer.Length);
 01011                    buffer.Slice(0, remainingInCurrentBuffer.Length).CopyTo(remainingInCurrentBuffer);
 01012                    buffer = buffer.Slice(remainingInCurrentBuffer.Length);
 1013
 01014                    newBuffer = ArrayPool<byte>.Shared.Rent(newBufferCapacity);
 01015                    buffer.CopyTo(newBuffer);
 01016                    _lastBufferOffset = buffer.Length;
 1017
 1018                    // Find the first empty slot in '_pooledBuffers', resizing the array if needed.
 01019                    int bufferCount = 0;
 01020                    if (_pooledBuffers is null)
 01021                    {
 1022                        // Starting with 4 buffers means we'll have capacity for at least
 1023                        // 16 KB + 32 KB + 64 KB + 128 KB + 256 KB (last buffer) = 496 KB
 01024                        _pooledBuffers = new byte[]?[4];
 01025                    }
 1026                    else
 01027                    {
 01028                        byte[]?[] buffers = _pooledBuffers;
 01029                        while (bufferCount < buffers.Length && buffers[bufferCount] is not null)
 01030                        {
 01031                            bufferCount++;
 01032                        }
 1033
 01034                        if (bufferCount == buffers.Length)
 01035                        {
 01036                            Debug.Assert(bufferCount <= 16);
 1037
 1038                            // After the first resize, we should have enough capacity for at least ~8 MB.
 1039                            // ~128 MB after the second, ~2 GB after the third.
 01040                            Array.Resize(ref _pooledBuffers, bufferCount + 4);
 01041                        }
 01042                    }
 1043
 01044                    _pooledBuffers[bufferCount] = _lastBuffer;
 01045                }
 1046
 01047                _lastBuffer = newBuffer;
 01048            }
 1049
 1050            public void CopyToCore(Span<byte> destination)
 01051            {
 01052                Debug.Assert(destination.Length >= _totalLength);
 1053
 01054                if (_pooledBuffers is byte[]?[] buffers)
 01055                {
 01056                    Debug.Assert(buffers.Length > 0 && buffers[0] is not null);
 1057
 01058                    foreach (byte[]? buffer in buffers)
 01059                    {
 01060                        if (buffer is null)
 01061                        {
 01062                            break;
 1063                        }
 1064
 01065                        Debug.Assert(destination.Length >= buffer.Length);
 1066
 01067                        buffer.CopyTo(destination);
 01068                        destination = destination.Slice(buffer.Length);
 01069                    }
 01070                }
 1071
 01072                Debug.Assert(_lastBufferOffset <= _lastBuffer.Length);
 01073                Debug.Assert(_lastBufferOffset <= destination.Length);
 1074
 01075                _lastBuffer.AsSpan(0, _lastBufferOffset).CopyTo(destination);
 01076            }
 1077
 1078            private void ReturnAllPooledBuffers()
 01079            {
 01080                if (_pooledBuffers is byte[]?[] buffers)
 01081                {
 01082                    _pooledBuffers = null;
 1083
 01084                    foreach (byte[]? buffer in buffers)
 01085                    {
 01086                        if (buffer is null)
 01087                        {
 01088                            break;
 1089                        }
 1090
 01091                        ArrayPool<byte>.Shared.Return(buffer);
 01092                    }
 01093                }
 1094
 01095                Debug.Assert(_lastBuffer is not null);
 01096                byte[] lastBuffer = _lastBuffer;
 01097                _lastBuffer = null!;
 1098
 01099                if (_lastBufferIsPooled)
 01100                {
 01101                    _lastBufferIsPooled = false;
 01102                    ArrayPool<byte>.Shared.Return(lastBuffer);
 01103                }
 01104            }
 1105
 1106            public override void Write(byte[] buffer, int offset, int count)
 01107            {
 01108                ValidateBufferArguments(buffer, offset, count);
 1109
 01110                Write(buffer.AsSpan(offset, count));
 01111            }
 1112
 1113            public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 01114            {
 01115                if (cancellationToken.IsCancellationRequested)
 01116                {
 01117                    return Task.FromCanceled(cancellationToken);
 1118                }
 1119
 01120                Write(buffer, offset, count);
 01121                return Task.CompletedTask;
 01122            }
 1123
 1124            public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = defa
 01125            {
 01126                if (cancellationToken.IsCancellationRequested)
 01127                {
 01128                    return ValueTask.FromCanceled(cancellationToken);
 1129                }
 1130
 01131                Write(buffer.Span);
 01132                return default;
 01133            }
 1134
 1135            public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? asyncCallback, 
 01136                TaskToAsyncResult.Begin(WriteAsync(buffer, offset, count, CancellationToken.None), asyncCallback, asyncS
 1137
 1138            public override void EndWrite(IAsyncResult asyncResult) =>
 01139                TaskToAsyncResult.End(asyncResult);
 1140
 1141            public override void WriteByte(byte value) =>
 01142                Write(new ReadOnlySpan<byte>(ref value));
 1143
 01144            public override void Flush() { }
 01145            public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask;
 1146
 01147            public override long Length => _totalLength;
 01148            public override bool CanWrite => true;
 01149            public override bool CanRead => false;
 01150            public override bool CanSeek => false;
 1151
 01152            public override long Position { get { throw new NotSupportedException(); } set { throw new NotSupportedExcep
 01153            public override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); }
 01154            public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
 01155            public override void SetLength(long value) { throw new NotSupportedException(); }
 1156        }
 1157    }
 1158}

Methods/Properties

.cctor()
UTF8Preamble()
UTF32Preamble()
UnicodePreamble()
BigEndianUnicodePreamble()
AssertEncodingConstants(System.Text.Encoding,System.Int32,System.ReadOnlySpan`1<System.Byte>)
Headers()
SetHeaders(System.Net.Http.Headers.HttpContentHeaders)
IsBuffered()
.ctor()
CreateMemoryStreamFromBufferedContent()
ReadAsStringAsync()
ReadAsStringAsync(System.Threading.CancellationToken)
ReadBufferedContentAsString()
ReadBufferAsString(System.Net.Http.HttpContent/LimitArrayPoolWriteStream,System.Net.Http.Headers.HttpContentHeaders)
ReadAsByteArrayAsync()
ReadAsByteArrayAsync(System.Threading.CancellationToken)
ReadBufferedContentAsByteArray()
ReadAsStream()
ReadAsStream(System.Threading.CancellationToken)
ReadAsStreamAsync()
ReadAsStreamAsync(System.Threading.CancellationToken)
TryReadAsStream()
SerializeToStream(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken)
SerializeToStreamAsync(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken)
AllowDuplex()
CopyTo(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken)
CopyToAsync(System.IO.Stream)
CopyToAsync(System.IO.Stream,System.Threading.CancellationToken)
CopyToAsync(System.IO.Stream,System.Net.TransportContext)
CopyToAsync(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken)
WaitAsync()
InternalCopyToAsync(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken)
LoadIntoBuffer(System.Int64,System.Threading.CancellationToken)
LoadIntoBufferAsync()
LoadIntoBufferAsync(System.Int64)
LoadIntoBufferAsync(System.Threading.CancellationToken)
LoadIntoBufferAsync(System.Int64,System.Threading.CancellationToken)
LoadIntoBufferAsyncCore()
CreateContentReadStream(System.Threading.CancellationToken)
CreateContentReadStreamAsync()
CreateContentReadStreamAsync(System.Threading.CancellationToken)
TryCreateContentReadStream()
GetComputedOrBufferLength()
CreateTemporaryBuffer(System.Int64,System.Net.Http.HttpContent/LimitArrayPoolWriteStream&,System.Exception&)
Dispose(System.Boolean)
Dispose()
CheckDisposed()
CheckTaskNotNull(System.Threading.Tasks.Task)
StreamCopyExceptionNeedsWrapping(System.Exception)
GetStreamCopyException(System.Exception)
WrapStreamCopyException(System.Exception)
GetPreambleLength(System.ReadOnlySpan`1<System.Byte>,System.Text.Encoding)
TryDetectEncoding(System.ReadOnlySpan`1<System.Byte>,System.Text.Encoding&,System.Int32&)
WaitAndReturnAsync()
CreateOverCapacityException(System.Int64)
.ctor(System.Int32,System.Int64,System.Boolean)
Finalize()
Dispose(System.Boolean)
ToArray()
GetSingleBuffer()
GetFirstBuffer()
CreateCopy()
ReallocateIfPooled()
Write(System.ReadOnlySpan`1<System.Byte>)
GrowAndWrite(System.ReadOnlySpan`1<System.Byte>)
CopyToCore(System.Span`1<System.Byte>)
ReturnAllPooledBuffers()
Write(System.Byte[],System.Int32,System.Int32)
WriteAsync(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken)
WriteAsync(System.ReadOnlyMemory`1<System.Byte>,System.Threading.CancellationToken)
BeginWrite(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object)
EndWrite(System.IAsyncResult)
WriteByte(System.Byte)
Flush()
FlushAsync(System.Threading.CancellationToken)
Length()
CanWrite()
CanRead()
CanSeek()
Position()
Read(System.Byte[],System.Int32,System.Int32)
Seek(System.Int64,System.IO.SeekOrigin)
SetLength(System.Int64)