< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 425
Coverable lines: 425
Total lines: 839
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 104
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%110%
.ctor(...)100%110%
.ctor()100%110%
.ctor(...)100%110%
GetStringAsync(...)100%110%
GetStringAsync(...)100%110%
GetStringAsync(...)100%110%
GetStringAsync(...)100%110%
GetStringAsyncCore(...)0%660%
GetByteArrayAsync(...)100%110%
GetByteArrayAsync(...)100%110%
GetByteArrayAsync(...)100%110%
GetByteArrayAsync(...)100%110%
GetByteArrayAsyncCore(...)0%660%
GetStreamAsync(...)100%110%
GetStreamAsync(...)100%110%
GetStreamAsync(...)100%110%
GetStreamAsync(...)100%110%
GetStreamAsyncCore(...)0%220%
GetAsync(...)100%110%
GetAsync(...)100%110%
GetAsync(...)100%110%
GetAsync(...)100%110%
GetAsync(...)100%110%
GetAsync(...)100%110%
GetAsync(...)100%110%
GetAsync(...)100%110%
PostAsync(...)100%110%
PostAsync(...)100%110%
PostAsync(...)100%110%
PostAsync(...)100%110%
PutAsync(...)100%110%
PutAsync(...)100%110%
PutAsync(...)100%110%
PutAsync(...)100%110%
PatchAsync(...)100%110%
PatchAsync(...)100%110%
PatchAsync(...)100%110%
PatchAsync(...)100%110%
DeleteAsync(...)100%110%
DeleteAsync(...)100%110%
DeleteAsync(...)100%110%
DeleteAsync(...)100%110%
Send(...)100%110%
Send(...)100%110%
Send(...)100%110%
Send(...)0%440%
SendAsync(...)100%110%
SendAsync(...)100%110%
SendAsync(...)100%110%
SendAsync(...)100%110%
Core(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpCompletionOption,System.Threading.CancellationTokenSource,System.Boolean,System.Threading.CancellationTokenSource,System.Threading.CancellationToken)0%440%
CheckRequestBeforeSend(...)100%110%
ThrowForNullResponse(...)0%220%
ShouldBufferResponse(...)0%220%
HandleFailure(...)0%24240%
StartSend(...)0%220%
FinishSend(...)0%660%
CancelPendingRequests()100%110%
Dispose(...)0%440%
SetOperationStarted()0%220%
CheckDisposedOrStarted()0%220%
CheckRequestMessage(...)0%220%
PrepareRequestMessage(...)0%14140%
PrepareCancellationTokenSource(...)0%660%
CreateUri(...)0%220%
CreateRequestMessage(...)100%110%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.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.Diagnostics;
 5using System.Diagnostics.CodeAnalysis;
 6using System.IO;
 7using System.Net.Http.Headers;
 8using System.Runtime.Versioning;
 9using System.Threading;
 10using System.Threading.Tasks;
 11
 12namespace System.Net.Http
 13{
 14    public partial class HttpClient : HttpMessageInvoker
 15    {
 16        #region Fields
 17
 18        private static IWebProxy? s_defaultProxy;
 019        private static readonly TimeSpan s_defaultTimeout = TimeSpan.FromSeconds(100);
 020        private static readonly TimeSpan s_maxTimeout = TimeSpan.FromMilliseconds(int.MaxValue);
 021        private static readonly TimeSpan s_infiniteTimeout = Threading.Timeout.InfiniteTimeSpan;
 22        private const HttpCompletionOption DefaultCompletionOption = HttpCompletionOption.ResponseContentRead;
 23
 24        private volatile bool _operationStarted;
 25        private volatile bool _disposed;
 26
 27        private CancellationTokenSource _pendingRequestsCts;
 28        private HttpRequestHeaders? _defaultRequestHeaders;
 029        private Version _defaultRequestVersion = HttpRequestMessage.DefaultRequestVersion;
 030        private HttpVersionPolicy _defaultVersionPolicy = HttpRequestMessage.DefaultVersionPolicy;
 31
 32        private Uri? _baseAddress;
 33        private TimeSpan _timeout;
 34        private int _maxResponseContentBufferSize;
 35
 36        #endregion Fields
 37
 38        #region Properties
 39        public static IWebProxy DefaultProxy
 40        {
 041            get => LazyInitializer.EnsureInitialized(ref s_defaultProxy, () => SystemProxyInfo.Proxy);
 42            set
 043            {
 044                ArgumentNullException.ThrowIfNull(value);
 045                s_defaultProxy = value;
 046            }
 47        }
 48
 49        public HttpRequestHeaders DefaultRequestHeaders =>
 050            _defaultRequestHeaders ??= new HttpRequestHeaders();
 51
 52        public Version DefaultRequestVersion
 53        {
 054            get => _defaultRequestVersion;
 55            set
 056            {
 057                CheckDisposedOrStarted();
 058                ArgumentNullException.ThrowIfNull(value);
 059                _defaultRequestVersion = value;
 060            }
 61        }
 62
 63        /// <summary>
 64        /// Gets or sets the default value of <see cref="HttpRequestMessage.VersionPolicy" /> for implicitly created req
 65        /// e.g.: <see cref="GetAsync(string?)" />, <see cref="PostAsync(string?, HttpContent)" />.
 66        /// </summary>
 67        /// <remarks>
 68        /// Note that this property has no effect on any of the <see cref="Send(HttpRequestMessage)" /> and <see cref="S
 69        /// since they accept fully initialized <see cref="HttpRequestMessage" />.
 70        /// </remarks>
 71        public HttpVersionPolicy DefaultVersionPolicy
 72        {
 073            get => _defaultVersionPolicy;
 74            set
 075            {
 076                if ((uint)value > (uint)HttpVersionPolicy.RequestVersionExact)
 077                {
 078                    throw new ArgumentException(SR.Format(SR.net_invalid_enum, nameof(HttpVersionPolicy)), nameof(value)
 79                }
 80
 081                CheckDisposedOrStarted();
 082                _defaultVersionPolicy = value;
 083            }
 84        }
 85
 86        public Uri? BaseAddress
 87        {
 088            get => _baseAddress;
 89            set
 090            {
 91                // It's OK to not have a base address specified, but if one is, it needs to be absolute.
 092                if (value is not null && !value.IsAbsoluteUri)
 093                {
 094                    throw new ArgumentException(SR.net_http_client_absolute_baseaddress_required, nameof(value));
 95                }
 96
 097                CheckDisposedOrStarted();
 98
 099                if (NetEventSource.Log.IsEnabled()) NetEventSource.UriBaseAddress(this, value);
 100
 0101                _baseAddress = value;
 0102            }
 103        }
 104
 105        public TimeSpan Timeout
 106        {
 0107            get => _timeout;
 108            set
 0109            {
 0110                if (value != s_infiniteTimeout)
 0111                {
 0112                    ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(value, TimeSpan.Zero);
 0113                    ArgumentOutOfRangeException.ThrowIfGreaterThan(value, s_maxTimeout);
 0114                }
 0115                CheckDisposedOrStarted();
 0116                _timeout = value;
 0117            }
 118        }
 119
 120        public long MaxResponseContentBufferSize
 121        {
 0122            get => _maxResponseContentBufferSize;
 123            set
 0124            {
 0125                ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value);
 0126                if (value > HttpContent.MaxBufferSize)
 0127                {
 0128                    throw new ArgumentOutOfRangeException(nameof(value), value,
 0129                        SR.Format(System.Globalization.CultureInfo.InvariantCulture,
 0130                        SR.net_http_content_buffersize_limit, HttpContent.MaxBufferSize));
 131                }
 0132                CheckDisposedOrStarted();
 133
 0134                Debug.Assert(HttpContent.MaxBufferSize <= int.MaxValue);
 0135                _maxResponseContentBufferSize = (int)value;
 0136            }
 137        }
 138
 139        #endregion Properties
 140
 141        #region Constructors
 142
 0143        public HttpClient() : this(new HttpClientHandler())
 0144        {
 0145        }
 146
 0147        public HttpClient(HttpMessageHandler handler) : this(handler, true)
 0148        {
 0149        }
 150
 0151        public HttpClient(HttpMessageHandler handler, bool disposeHandler) : base(handler, disposeHandler)
 0152        {
 0153            _timeout = s_defaultTimeout;
 0154            _maxResponseContentBufferSize = HttpContent.MaxBufferSize;
 0155            _pendingRequestsCts = new CancellationTokenSource();
 0156        }
 157
 158        #endregion Constructors
 159
 160        #region Public Send
 161
 162        #region Simple Get Overloads
 163
 164        public Task<string> GetStringAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri) =>
 0165            GetStringAsync(CreateUri(requestUri));
 166
 167        public Task<string> GetStringAsync(Uri? requestUri) =>
 0168            GetStringAsync(requestUri, CancellationToken.None);
 169
 170        public Task<string> GetStringAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, CancellationTok
 0171            GetStringAsync(CreateUri(requestUri), cancellationToken);
 172
 173        public Task<string> GetStringAsync(Uri? requestUri, CancellationToken cancellationToken)
 0174        {
 0175            HttpRequestMessage request = CreateRequestMessage(HttpMethod.Get, requestUri);
 176
 177            // Called outside of async state machine to propagate certain exception even without awaiting the returned t
 0178            CheckRequestBeforeSend(request);
 179
 0180            return GetStringAsyncCore(request, cancellationToken);
 0181        }
 182
 183        private async Task<string> GetStringAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)
 0184        {
 0185            bool telemetryStarted = StartSend(request);
 0186            bool responseContentTelemetryStarted = false;
 187
 0188            (CancellationTokenSource cts, bool disposeCts, CancellationTokenSource pendingRequestsCts) = PrepareCancella
 0189            HttpResponseMessage? response = null;
 0190            HttpContent.LimitArrayPoolWriteStream? buffer = null;
 191            try
 0192            {
 193                // Wait for the response message and make sure it completed successfully.
 0194                response = await base.SendAsync(request, cts.Token).ConfigureAwait(false);
 0195                ThrowForNullResponse(response);
 0196                response.EnsureSuccessStatusCode();
 197
 198                // Get the response content.
 0199                HttpContent c = response.Content;
 0200                if (HttpTelemetry.Log.IsEnabled() && telemetryStarted)
 0201                {
 0202                    HttpTelemetry.Log.ResponseContentStart();
 0203                    responseContentTelemetryStarted = true;
 0204                }
 205
 206                // Since the underlying byte[] will never be exposed, we use an ArrayPool-backed
 207                // stream to which we copy all of the data from the response.
 0208                buffer = new HttpContent.LimitArrayPoolWriteStream(
 0209                    _maxResponseContentBufferSize,
 0210                    c.Headers.ContentLength.GetValueOrDefault(),
 0211                    getFinalSizeFromPool: true);
 212
 0213                using Stream responseStream = c.TryReadAsStream() ?? await c.ReadAsStreamAsync(cts.Token).ConfigureAwait
 214                try
 0215                {
 0216                    await responseStream.CopyToAsync(buffer, cts.Token).ConfigureAwait(false);
 0217                }
 0218                catch (Exception e) when (HttpContent.StreamCopyExceptionNeedsWrapping(e))
 0219                {
 0220                    throw HttpContent.WrapStreamCopyException(e);
 221                }
 222
 223                // Decode and return the data from the buffer.
 0224                return HttpContent.ReadBufferAsString(buffer, c.Headers);
 225            }
 0226            catch (Exception e)
 0227            {
 0228                HandleFailure(e, telemetryStarted, response, cts, cancellationToken, pendingRequestsCts);
 0229                throw;
 230            }
 231            finally
 0232            {
 0233                buffer?.ReturnAllPooledBuffers();
 0234                FinishSend(response, cts, disposeCts, telemetryStarted, responseContentTelemetryStarted);
 0235            }
 0236        }
 237
 238        public Task<byte[]> GetByteArrayAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri) =>
 0239            GetByteArrayAsync(CreateUri(requestUri));
 240
 241        public Task<byte[]> GetByteArrayAsync(Uri? requestUri) =>
 0242            GetByteArrayAsync(requestUri, CancellationToken.None);
 243
 244        public Task<byte[]> GetByteArrayAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, Cancellation
 0245            GetByteArrayAsync(CreateUri(requestUri), cancellationToken);
 246
 247        public Task<byte[]> GetByteArrayAsync(Uri? requestUri, CancellationToken cancellationToken)
 0248        {
 0249            HttpRequestMessage request = CreateRequestMessage(HttpMethod.Get, requestUri);
 250
 251            // Called outside of async state machine to propagate certain exception even without awaiting the returned t
 0252            CheckRequestBeforeSend(request);
 253
 0254            return GetByteArrayAsyncCore(request, cancellationToken);
 0255        }
 256
 257        private async Task<byte[]> GetByteArrayAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken
 0258        {
 0259            bool telemetryStarted = StartSend(request);
 0260            bool responseContentTelemetryStarted = false;
 261
 0262            (CancellationTokenSource cts, bool disposeCts, CancellationTokenSource pendingRequestsCts) = PrepareCancella
 0263            HttpResponseMessage? response = null;
 0264            HttpContent.LimitArrayPoolWriteStream? buffer = null;
 265            try
 0266            {
 267                // Wait for the response message and make sure it completed successfully.
 0268                response = await base.SendAsync(request, cts.Token).ConfigureAwait(false);
 0269                ThrowForNullResponse(response);
 0270                response.EnsureSuccessStatusCode();
 271
 272                // Get the response content.
 0273                HttpContent c = response.Content;
 0274                if (HttpTelemetry.Log.IsEnabled() && telemetryStarted)
 0275                {
 0276                    HttpTelemetry.Log.ResponseContentStart();
 0277                    responseContentTelemetryStarted = true;
 0278                }
 279
 280                // If we got a content length, then we assume that it's correct. If that's the case,
 281                // we can opportunistically allocate the exact-sized buffer while buffering the content.
 282                // If we didn't get a content length, then we assume we're going to have to grow
 283                // the buffer potentially several times and that it's unlikely the underlying buffer
 284                // at the end will be the exact size needed, in which case it's more beneficial to use
 285                // ArrayPool buffers and copy out to a new array at the end.
 0286                buffer = new HttpContent.LimitArrayPoolWriteStream(
 0287                    _maxResponseContentBufferSize,
 0288                    c.Headers.ContentLength.GetValueOrDefault(),
 0289                    getFinalSizeFromPool: false);
 290
 0291                using Stream responseStream = c.TryReadAsStream() ?? await c.ReadAsStreamAsync(cts.Token).ConfigureAwait
 292                try
 0293                {
 0294                    await responseStream.CopyToAsync(buffer, cts.Token).ConfigureAwait(false);
 0295                }
 0296                catch (Exception e) when (HttpContent.StreamCopyExceptionNeedsWrapping(e))
 0297                {
 0298                    throw HttpContent.WrapStreamCopyException(e);
 299                }
 300
 0301                return buffer.ToArray();
 302            }
 0303            catch (Exception e)
 0304            {
 0305                HandleFailure(e, telemetryStarted, response, cts, cancellationToken, pendingRequestsCts);
 0306                throw;
 307            }
 308            finally
 0309            {
 0310                buffer?.ReturnAllPooledBuffers();
 0311                FinishSend(response, cts, disposeCts, telemetryStarted, responseContentTelemetryStarted);
 0312            }
 0313        }
 314
 315        public Task<Stream> GetStreamAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri) =>
 0316            GetStreamAsync(CreateUri(requestUri));
 317
 318        public Task<Stream> GetStreamAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, CancellationTok
 0319            GetStreamAsync(CreateUri(requestUri), cancellationToken);
 320
 321        public Task<Stream> GetStreamAsync(Uri? requestUri) =>
 0322            GetStreamAsync(requestUri, CancellationToken.None);
 323
 324        public Task<Stream> GetStreamAsync(Uri? requestUri, CancellationToken cancellationToken)
 0325        {
 0326            HttpRequestMessage request = CreateRequestMessage(HttpMethod.Get, requestUri);
 327
 328            // Called outside of async state machine to propagate certain exception even without awaiting the returned t
 0329            CheckRequestBeforeSend(request);
 330
 0331            return GetStreamAsyncCore(request, cancellationToken);
 0332        }
 333
 334        private async Task<Stream> GetStreamAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)
 0335        {
 0336            bool telemetryStarted = StartSend(request);
 337
 0338            (CancellationTokenSource cts, bool disposeCts, CancellationTokenSource pendingRequestsCts) = PrepareCancella
 0339            HttpResponseMessage? response = null;
 340            try
 0341            {
 342                // Wait for the response message and make sure it completed successfully.
 0343                response = await base.SendAsync(request, cts.Token).ConfigureAwait(false);
 0344                ThrowForNullResponse(response);
 0345                response.EnsureSuccessStatusCode();
 346
 0347                HttpContent c = response.Content;
 0348                return c.TryReadAsStream() ?? await c.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
 349            }
 0350            catch (Exception e)
 0351            {
 0352                HandleFailure(e, telemetryStarted, response, cts, cancellationToken, pendingRequestsCts);
 0353                throw;
 354            }
 355            finally
 0356            {
 0357                FinishSend(response, cts, disposeCts, telemetryStarted, responseContentTelemetryStarted: false);
 0358            }
 0359        }
 360
 361        #endregion Simple Get Overloads
 362
 363        #region REST Send Overloads
 364
 365        public Task<HttpResponseMessage> GetAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri) =>
 0366            GetAsync(CreateUri(requestUri));
 367
 368        public Task<HttpResponseMessage> GetAsync(Uri? requestUri) =>
 0369            GetAsync(requestUri, DefaultCompletionOption);
 370
 371        public Task<HttpResponseMessage> GetAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, HttpComp
 0372            GetAsync(CreateUri(requestUri), completionOption);
 373
 374        public Task<HttpResponseMessage> GetAsync(Uri? requestUri, HttpCompletionOption completionOption) =>
 0375            GetAsync(requestUri, completionOption, CancellationToken.None);
 376
 377        public Task<HttpResponseMessage> GetAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, Cancella
 0378            GetAsync(CreateUri(requestUri), cancellationToken);
 379
 380        public Task<HttpResponseMessage> GetAsync(Uri? requestUri, CancellationToken cancellationToken) =>
 0381            GetAsync(requestUri, DefaultCompletionOption, cancellationToken);
 382
 383        public Task<HttpResponseMessage> GetAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, HttpComp
 0384            GetAsync(CreateUri(requestUri), completionOption, cancellationToken);
 385
 386        public Task<HttpResponseMessage> GetAsync(Uri? requestUri, HttpCompletionOption completionOption, CancellationTo
 0387            SendAsync(CreateRequestMessage(HttpMethod.Get, requestUri), completionOption, cancellationToken);
 388
 389        public Task<HttpResponseMessage> PostAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, HttpCon
 0390            PostAsync(CreateUri(requestUri), content);
 391
 392        public Task<HttpResponseMessage> PostAsync(Uri? requestUri, HttpContent? content) =>
 0393            PostAsync(requestUri, content, CancellationToken.None);
 394
 395        public Task<HttpResponseMessage> PostAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, HttpCon
 0396            PostAsync(CreateUri(requestUri), content, cancellationToken);
 397
 398        public Task<HttpResponseMessage> PostAsync(Uri? requestUri, HttpContent? content, CancellationToken cancellation
 0399        {
 0400            HttpRequestMessage request = CreateRequestMessage(HttpMethod.Post, requestUri);
 0401            request.Content = content;
 0402            return SendAsync(request, cancellationToken);
 0403        }
 404
 405        public Task<HttpResponseMessage> PutAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, HttpCont
 0406            PutAsync(CreateUri(requestUri), content);
 407
 408        public Task<HttpResponseMessage> PutAsync(Uri? requestUri, HttpContent? content) =>
 0409            PutAsync(requestUri, content, CancellationToken.None);
 410
 411        public Task<HttpResponseMessage> PutAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, HttpCont
 0412            PutAsync(CreateUri(requestUri), content, cancellationToken);
 413
 414        public Task<HttpResponseMessage> PutAsync(Uri? requestUri, HttpContent? content, CancellationToken cancellationT
 0415        {
 0416            HttpRequestMessage request = CreateRequestMessage(HttpMethod.Put, requestUri);
 0417            request.Content = content;
 0418            return SendAsync(request, cancellationToken);
 0419        }
 420
 421        public Task<HttpResponseMessage> PatchAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, HttpCo
 0422            PatchAsync(CreateUri(requestUri), content);
 423
 424        public Task<HttpResponseMessage> PatchAsync(Uri? requestUri, HttpContent? content) =>
 0425            PatchAsync(requestUri, content, CancellationToken.None);
 426
 427        public Task<HttpResponseMessage> PatchAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, HttpCo
 0428            PatchAsync(CreateUri(requestUri), content, cancellationToken);
 429
 430        public Task<HttpResponseMessage> PatchAsync(Uri? requestUri, HttpContent? content, CancellationToken cancellatio
 0431        {
 0432            HttpRequestMessage request = CreateRequestMessage(HttpMethod.Patch, requestUri);
 0433            request.Content = content;
 0434            return SendAsync(request, cancellationToken);
 0435        }
 436
 437        public Task<HttpResponseMessage> DeleteAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri) =>
 0438            DeleteAsync(CreateUri(requestUri));
 439
 440        public Task<HttpResponseMessage> DeleteAsync(Uri? requestUri) =>
 0441            DeleteAsync(requestUri, CancellationToken.None);
 442
 443        public Task<HttpResponseMessage> DeleteAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, Cance
 0444            DeleteAsync(CreateUri(requestUri), cancellationToken);
 445
 446        public Task<HttpResponseMessage> DeleteAsync(Uri? requestUri, CancellationToken cancellationToken) =>
 0447            SendAsync(CreateRequestMessage(HttpMethod.Delete, requestUri), cancellationToken);
 448
 449        #endregion REST Send Overloads
 450
 451        #region Advanced Send Overloads
 452
 453        [UnsupportedOSPlatform("android")]
 454        [UnsupportedOSPlatform("browser")]
 455        [UnsupportedOSPlatform("ios")]
 456        [UnsupportedOSPlatform("tvos")]
 457        public HttpResponseMessage Send(HttpRequestMessage request) =>
 0458            Send(request, DefaultCompletionOption, cancellationToken: default);
 459
 460        [UnsupportedOSPlatform("android")]
 461        [UnsupportedOSPlatform("browser")]
 462        [UnsupportedOSPlatform("ios")]
 463        [UnsupportedOSPlatform("tvos")]
 464        public HttpResponseMessage Send(HttpRequestMessage request, HttpCompletionOption completionOption) =>
 0465            Send(request, completionOption, cancellationToken: default);
 466
 467        [UnsupportedOSPlatform("android")]
 468        [UnsupportedOSPlatform("browser")]
 469        [UnsupportedOSPlatform("ios")]
 470        [UnsupportedOSPlatform("tvos")]
 471        public override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) =>
 0472            Send(request, DefaultCompletionOption, cancellationToken);
 473
 474        [UnsupportedOSPlatform("android")]
 475        [UnsupportedOSPlatform("browser")]
 476        [UnsupportedOSPlatform("ios")]
 477        [UnsupportedOSPlatform("tvos")]
 478        public HttpResponseMessage Send(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationT
 0479        {
 0480            CheckRequestBeforeSend(request);
 0481            (CancellationTokenSource cts, bool disposeCts, CancellationTokenSource pendingRequestsCts) = PrepareCancella
 482
 0483            bool telemetryStarted = StartSend(request);
 0484            bool responseContentTelemetryStarted = false;
 0485            HttpResponseMessage? response = null;
 486            try
 0487            {
 488                // Wait for the send request to complete, getting back the response.
 0489                response = base.Send(request, cts.Token);
 0490                ThrowForNullResponse(response);
 491
 492                // Buffer the response content if we've been asked to.
 0493                if (ShouldBufferResponse(completionOption, request))
 0494                {
 0495                    if (HttpTelemetry.Log.IsEnabled() && telemetryStarted)
 0496                    {
 0497                        HttpTelemetry.Log.ResponseContentStart();
 0498                        responseContentTelemetryStarted = true;
 0499                    }
 500
 0501                    response.Content.LoadIntoBuffer(_maxResponseContentBufferSize, cts.Token);
 0502                }
 503
 0504                return response;
 505            }
 0506            catch (Exception e)
 0507            {
 0508                HandleFailure(e, telemetryStarted, response, cts, cancellationToken, pendingRequestsCts);
 0509                throw;
 510            }
 511            finally
 0512            {
 0513                FinishSend(response, cts, disposeCts, telemetryStarted, responseContentTelemetryStarted);
 0514            }
 0515        }
 516
 517        public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request) =>
 0518            SendAsync(request, DefaultCompletionOption, CancellationToken.None);
 519
 520        public override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationTo
 0521            SendAsync(request, DefaultCompletionOption, cancellationToken);
 522
 523        public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption) =>
 0524            SendAsync(request, completionOption, CancellationToken.None);
 525
 526        public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, Ca
 0527        {
 528            // Called outside of async state machine to propagate certain exception even without awaiting the returned t
 0529            CheckRequestBeforeSend(request);
 0530            (CancellationTokenSource cts, bool disposeCts, CancellationTokenSource pendingRequestsCts) = PrepareCancella
 531
 0532            return Core(request, completionOption, cts, disposeCts, pendingRequestsCts, cancellationToken);
 533
 534            async Task<HttpResponseMessage> Core(
 535                HttpRequestMessage request, HttpCompletionOption completionOption,
 536                CancellationTokenSource cts, bool disposeCts, CancellationTokenSource pendingRequestsCts, CancellationTo
 0537            {
 0538                bool telemetryStarted = StartSend(request);
 0539                bool responseContentTelemetryStarted = false;
 0540                HttpResponseMessage? response = null;
 541                try
 0542                {
 543                    // Wait for the send request to complete, getting back the response.
 0544                    response = await base.SendAsync(request, cts.Token).ConfigureAwait(false);
 0545                    ThrowForNullResponse(response);
 546
 547                    // Buffer the response content if we've been asked to.
 0548                    if (ShouldBufferResponse(completionOption, request))
 0549                    {
 0550                        if (HttpTelemetry.Log.IsEnabled() && telemetryStarted)
 0551                        {
 0552                            HttpTelemetry.Log.ResponseContentStart();
 0553                            responseContentTelemetryStarted = true;
 0554                        }
 555
 0556                        await response.Content.LoadIntoBufferAsync(_maxResponseContentBufferSize, cts.Token).ConfigureAw
 0557                    }
 558
 0559                    return response;
 560                }
 0561                catch (Exception e)
 0562                {
 0563                    HandleFailure(e, telemetryStarted, response, cts, originalCancellationToken, pendingRequestsCts);
 0564                    throw;
 565                }
 566                finally
 0567                {
 0568                    FinishSend(response, cts, disposeCts, telemetryStarted, responseContentTelemetryStarted);
 0569                }
 0570            }
 0571        }
 572
 573        private void CheckRequestBeforeSend(HttpRequestMessage request)
 0574        {
 0575            ArgumentNullException.ThrowIfNull(request);
 576
 0577            ObjectDisposedException.ThrowIf(_disposed, this);
 0578            CheckRequestMessage(request);
 579
 0580            SetOperationStarted();
 581
 582            // PrepareRequestMessage will resolve the request address against the base address.
 0583            PrepareRequestMessage(request);
 0584        }
 585
 586        private static void ThrowForNullResponse([NotNull] HttpResponseMessage? response)
 0587        {
 0588            if (response is null)
 0589            {
 0590                throw new InvalidOperationException(SR.net_http_handler_noresponse);
 591            }
 0592        }
 593
 594        private static bool ShouldBufferResponse(HttpCompletionOption completionOption, HttpRequestMessage request) =>
 0595            completionOption == HttpCompletionOption.ResponseContentRead &&
 0596            !string.Equals(request.Method.Method, "HEAD", StringComparison.OrdinalIgnoreCase);
 597
 598        private void HandleFailure(Exception e, bool telemetryStarted, HttpResponseMessage? response, CancellationTokenS
 0599        {
 0600            response?.Dispose();
 601
 0602            Exception? toThrow = null;
 603
 0604            if (e is OperationCanceledException oce)
 0605            {
 0606                if (cancellationToken.IsCancellationRequested)
 0607                {
 0608                    if (oce.CancellationToken != cancellationToken)
 0609                    {
 610                        // We got a cancellation exception, and the caller requested cancellation, but the exception doe
 611                        // Massage things so that the cancellation exception we propagate appropriately contains the cal
 612                        // multiple things caused cancellation, in which case we can attribute it to the caller's token,
 613                        // exception contains the linked token source, in which case that token isn't meaningful to the 
 0614                        e = toThrow = new TaskCanceledException(oce.Message, oce, cancellationToken);
 0615                    }
 0616                }
 0617                else if (cts.IsCancellationRequested && !pendingRequestsCts.IsCancellationRequested)
 0618                {
 619                    // If the linked cancellation token source was canceled, but cancellation wasn't requested, either b
 620                    // the only other cause could be a timeout.  Treat it as such.
 621
 622                    // cancellationToken could have been triggered right after we checked it, but before we checked the 
 623                    // We must check it again to avoid reporting a timeout when one did not occur.
 0624                    if (!cancellationToken.IsCancellationRequested)
 0625                    {
 0626                        Debug.Assert(_timeout.TotalSeconds > 0);
 0627                        e = toThrow = new TaskCanceledException(SR.Format(SR.net_http_request_timedout, _timeout.TotalSe
 0628                    }
 0629                }
 0630            }
 0631            else if (e is HttpRequestException && cts.IsCancellationRequested) // if cancellationToken is canceled, cts 
 0632            {
 633                // If the cancellation token source was canceled, race conditions abound, and we consider the failure to
 634                // caused by the cancellation (e.g. WebException when reading from canceled response stream).
 0635                e = toThrow = CancellationHelper.CreateOperationCanceledException(e, cancellationToken.IsCancellationReq
 0636            }
 637
 0638            LogRequestFailed(e, telemetryStarted);
 639
 0640            if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, e);
 641
 0642            if (toThrow != null)
 0643            {
 0644                throw toThrow;
 645            }
 0646        }
 647
 648        private static bool StartSend(HttpRequestMessage request)
 0649        {
 0650            if (HttpTelemetry.Log.IsEnabled())
 0651            {
 0652                HttpTelemetry.Log.RequestStart(request);
 0653                return true;
 654            }
 655
 0656            return false;
 0657        }
 658
 659        private static void FinishSend(HttpResponseMessage? response, CancellationTokenSource cts, bool disposeCts, bool
 0660        {
 661            // Log completion.
 0662            if (HttpTelemetry.Log.IsEnabled() && telemetryStarted)
 0663            {
 0664                if (responseContentTelemetryStarted)
 0665                {
 0666                    HttpTelemetry.Log.ResponseContentStop();
 0667                }
 668
 0669                HttpTelemetry.Log.RequestStop(response);
 0670            }
 671
 672            // Dispose of the CancellationTokenSource if it was created specially for this request
 673            // rather than being used across multiple requests.
 0674            if (disposeCts)
 0675            {
 0676                cts.Dispose();
 0677            }
 678
 679            // This method used to also dispose of the request content, e.g.:
 680            //     request.Content?.Dispose();
 681            // This has multiple problems:
 682            //   1. It prevents code from reusing request content objects for subsequent requests,
 683            //      as disposing of the object likely invalidates it for further use.
 684            //   2. It prevents the possibility of partial or full duplex communication, even if supported
 685            //      by the handler, as the request content may still be in use even if the response
 686            //      (or response headers) has been received.
 687            // By changing this to not dispose of the request content, disposal may end up being
 688            // left for the finalizer to handle, or the developer can explicitly dispose of the
 689            // content when they're done with it.  But it allows request content to be reused,
 690            // and more importantly it enables handlers that allow receiving of the response before
 691            // fully sending the request.  Prior to this change, a handler that supported duplex communication
 692            // would fail trying to access certain sites, if the site sent its response before it had
 693            // completely received the request: CurlHandler might then find that the request content
 694            // was disposed of while it still needed to read from it.
 0695        }
 696
 697        public void CancelPendingRequests()
 0698        {
 0699            ObjectDisposedException.ThrowIf(_disposed, this);
 700
 701            // With every request we link this cancellation token source.
 0702            CancellationTokenSource currentCts = Interlocked.Exchange(ref _pendingRequestsCts, new CancellationTokenSour
 703
 0704            currentCts.Cancel();
 0705            currentCts.Dispose();
 0706        }
 707
 708        #endregion Advanced Send Overloads
 709
 710        #endregion Public Send
 711
 712        #region IDisposable Members
 713
 714        protected override void Dispose(bool disposing)
 0715        {
 0716            if (disposing && !_disposed)
 0717            {
 0718                _disposed = true;
 719
 720                // Cancel all pending requests (if any). Note that we don't call CancelPendingRequests() but cancel
 721                // the CTS directly. The reason is that CancelPendingRequests() would cancel the current CTS and create
 722                // a new CTS. We don't want a new CTS in this case.
 0723                _pendingRequestsCts.Cancel();
 0724                _pendingRequestsCts.Dispose();
 0725            }
 726
 0727            base.Dispose(disposing);
 0728        }
 729
 730        #endregion
 731
 732        #region Private Helpers
 733
 734        private void SetOperationStarted()
 0735        {
 736            // This method flags the HttpClient instances as "active". I.e. we executed at least one request (or are
 737            // in the process of doing so). This information is used to lock-down all property setters. Once a
 738            // Send/SendAsync operation started, no property can be changed.
 0739            if (!_operationStarted)
 0740            {
 0741                _operationStarted = true;
 0742            }
 0743        }
 744
 745        private void CheckDisposedOrStarted()
 0746        {
 0747            ObjectDisposedException.ThrowIf(_disposed, this);
 0748            if (_operationStarted)
 0749            {
 0750                throw new InvalidOperationException(SR.net_http_operation_started);
 751            }
 0752        }
 753
 754        private static void CheckRequestMessage(HttpRequestMessage request)
 0755        {
 0756            if (!request.MarkAsSent())
 0757            {
 0758                throw new InvalidOperationException(SR.net_http_client_request_already_sent);
 759            }
 0760        }
 761
 762        private void PrepareRequestMessage(HttpRequestMessage request)
 0763        {
 0764            Uri? requestUri = null;
 0765            if ((request.RequestUri == null) && (_baseAddress == null))
 0766            {
 0767                throw new InvalidOperationException(SR.net_http_client_invalid_requesturi);
 768            }
 0769            if (request.RequestUri == null)
 0770            {
 0771                requestUri = _baseAddress;
 0772            }
 773            else
 0774            {
 775                // If the request Uri is an absolute Uri, just use it. Otherwise try to combine it with the base Uri.
 0776                if (!request.RequestUri.IsAbsoluteUri)
 0777                {
 0778                    if (_baseAddress == null)
 0779                    {
 0780                        throw new InvalidOperationException(SR.net_http_client_invalid_requesturi);
 781                    }
 782                    else
 0783                    {
 0784                        requestUri = new Uri(_baseAddress, request.RequestUri);
 0785                    }
 0786                }
 0787            }
 788
 789            // We modified the original request Uri. Assign the new Uri to the request message.
 0790            if (requestUri != null)
 0791            {
 0792                request.RequestUri = requestUri;
 0793            }
 794
 795            // Add default headers
 0796            if (_defaultRequestHeaders != null)
 0797            {
 0798                request.Headers.AddHeaders(_defaultRequestHeaders);
 0799            }
 0800        }
 801
 802        private (CancellationTokenSource TokenSource, bool DisposeTokenSource, CancellationTokenSource PendingRequestsCt
 0803        {
 804            // We need a CancellationTokenSource to use with the request.  We always have the global
 805            // _pendingRequestsCts to use, plus we may have a token provided by the caller, and we may
 806            // have a timeout.  If we have a timeout or a caller-provided token, we need to create a new
 807            // CTS (we can't, for example, timeout the pending requests CTS, as that could cancel other
 808            // unrelated operations).  Otherwise, we can use the pending requests CTS directly.
 809
 810            // Snapshot the current pending requests cancellation source. It can change concurrently due to cancellation
 811            // and it being replaced, and we need a stable view of it: if cancellation occurs and the caller's token has
 812            // it's either due to this source or due to the timeout, and checking whether this source is the culprit is 
 813            // it's more approximate checking elapsed time.
 0814            CancellationTokenSource pendingRequestsCts = _pendingRequestsCts;
 815
 0816            bool hasTimeout = _timeout != s_infiniteTimeout;
 0817            if (hasTimeout || cancellationToken.CanBeCanceled)
 0818            {
 0819                CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, pending
 0820                if (hasTimeout)
 0821                {
 0822                    cts.CancelAfter(_timeout);
 0823                }
 824
 0825                return (cts, DisposeTokenSource: true, pendingRequestsCts);
 826            }
 827
 0828            return (pendingRequestsCts, DisposeTokenSource: false, pendingRequestsCts);
 0829        }
 830
 831        private static Uri? CreateUri(string? uri) =>
 0832            string.IsNullOrEmpty(uri) ? null : new Uri(uri, UriKind.RelativeOrAbsolute);
 833
 834        private HttpRequestMessage CreateRequestMessage(HttpMethod method, Uri? uri) =>
 0835            new HttpRequestMessage(method, uri) { Version = _defaultRequestVersion, VersionPolicy = _defaultVersionPolic
 836        #endregion Private Helpers
 837    }
 838}
 839

Methods/Properties

.cctor()
.ctor(System.Net.Http.HttpMessageHandler,System.Boolean)
DefaultProxy()
DefaultProxy(System.Net.IWebProxy)
DefaultRequestHeaders()
DefaultRequestVersion()
DefaultRequestVersion(System.Version)
DefaultVersionPolicy()
DefaultVersionPolicy(System.Net.Http.HttpVersionPolicy)
BaseAddress()
BaseAddress(System.Uri)
Timeout()
Timeout(System.TimeSpan)
MaxResponseContentBufferSize()
MaxResponseContentBufferSize(System.Int64)
.ctor()
.ctor(System.Net.Http.HttpMessageHandler)
GetStringAsync(System.String)
GetStringAsync(System.Uri)
GetStringAsync(System.String,System.Threading.CancellationToken)
GetStringAsync(System.Uri,System.Threading.CancellationToken)
GetStringAsyncCore(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken)
GetByteArrayAsync(System.String)
GetByteArrayAsync(System.Uri)
GetByteArrayAsync(System.String,System.Threading.CancellationToken)
GetByteArrayAsync(System.Uri,System.Threading.CancellationToken)
GetByteArrayAsyncCore(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken)
GetStreamAsync(System.String)
GetStreamAsync(System.String,System.Threading.CancellationToken)
GetStreamAsync(System.Uri)
GetStreamAsync(System.Uri,System.Threading.CancellationToken)
GetStreamAsyncCore(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken)
GetAsync(System.String)
GetAsync(System.Uri)
GetAsync(System.String,System.Net.Http.HttpCompletionOption)
GetAsync(System.Uri,System.Net.Http.HttpCompletionOption)
GetAsync(System.String,System.Threading.CancellationToken)
GetAsync(System.Uri,System.Threading.CancellationToken)
GetAsync(System.String,System.Net.Http.HttpCompletionOption,System.Threading.CancellationToken)
GetAsync(System.Uri,System.Net.Http.HttpCompletionOption,System.Threading.CancellationToken)
PostAsync(System.String,System.Net.Http.HttpContent)
PostAsync(System.Uri,System.Net.Http.HttpContent)
PostAsync(System.String,System.Net.Http.HttpContent,System.Threading.CancellationToken)
PostAsync(System.Uri,System.Net.Http.HttpContent,System.Threading.CancellationToken)
PutAsync(System.String,System.Net.Http.HttpContent)
PutAsync(System.Uri,System.Net.Http.HttpContent)
PutAsync(System.String,System.Net.Http.HttpContent,System.Threading.CancellationToken)
PutAsync(System.Uri,System.Net.Http.HttpContent,System.Threading.CancellationToken)
PatchAsync(System.String,System.Net.Http.HttpContent)
PatchAsync(System.Uri,System.Net.Http.HttpContent)
PatchAsync(System.String,System.Net.Http.HttpContent,System.Threading.CancellationToken)
PatchAsync(System.Uri,System.Net.Http.HttpContent,System.Threading.CancellationToken)
DeleteAsync(System.String)
DeleteAsync(System.Uri)
DeleteAsync(System.String,System.Threading.CancellationToken)
DeleteAsync(System.Uri,System.Threading.CancellationToken)
Send(System.Net.Http.HttpRequestMessage)
Send(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpCompletionOption)
Send(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken)
Send(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpCompletionOption,System.Threading.CancellationToken)
SendAsync(System.Net.Http.HttpRequestMessage)
SendAsync(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken)
SendAsync(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpCompletionOption)
SendAsync(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpCompletionOption,System.Threading.CancellationToken)
Core(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpCompletionOption,System.Threading.CancellationTokenSource,System.Boolean,System.Threading.CancellationTokenSource,System.Threading.CancellationToken)
CheckRequestBeforeSend(System.Net.Http.HttpRequestMessage)
ThrowForNullResponse(System.Net.Http.HttpResponseMessage)
ShouldBufferResponse(System.Net.Http.HttpCompletionOption,System.Net.Http.HttpRequestMessage)
HandleFailure(System.Exception,System.Boolean,System.Net.Http.HttpResponseMessage,System.Threading.CancellationTokenSource,System.Threading.CancellationToken,System.Threading.CancellationTokenSource)
StartSend(System.Net.Http.HttpRequestMessage)
FinishSend(System.Net.Http.HttpResponseMessage,System.Threading.CancellationTokenSource,System.Boolean,System.Boolean,System.Boolean)
CancelPendingRequests()
Dispose(System.Boolean)
SetOperationStarted()
CheckDisposedOrStarted()
CheckRequestMessage(System.Net.Http.HttpRequestMessage)
PrepareRequestMessage(System.Net.Http.HttpRequestMessage)
PrepareCancellationTokenSource(System.Threading.CancellationToken)
CreateUri(System.String)
CreateRequestMessage(System.Net.Http.HttpMethod,System.Uri)