< Summary

Line coverage
10%
Covered lines: 13
Uncovered lines: 110
Coverable lines: 123
Total lines: 294
Line coverage: 10.5%
Branch coverage
3%
Covered branches: 1
Total branches: 32
Branch coverage: 3.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestMessage.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.Collections.Generic;
 5using System.Diagnostics;
 6using System.Diagnostics.CodeAnalysis;
 7using System.Net.Http.Headers;
 8using System.Text;
 9
 10namespace System.Net.Http
 11{
 12    public class HttpRequestMessage : IDisposable
 13    {
 114        internal static Version DefaultRequestVersion => HttpVersion.Version11;
 15        internal static HttpVersionPolicy DefaultVersionPolicy => HttpVersionPolicy.RequestVersionOrLower;
 16
 17        [Flags]
 18        private enum MessageFlags
 19        {
 20            AlreadySent = 1,
 21            PropagatorStateInjectedByDiagnosticsHandler = 2,
 22            Disposed = 4,
 23            AuthDisabled = 8,
 24            ConnectionIdSet = 16,
 25            DoNotPartitionConnectionPoolBySni = 32,
 26        }
 27
 28        private MessageFlags _flags;
 29
 30        private long _connectionId;
 31
 32        private HttpMethod _method;
 33        private Uri? _requestUri;
 34        private HttpRequestHeaders? _headers;
 35        private Version _version;
 36        private HttpVersionPolicy _versionPolicy;
 37        private HttpContent? _content;
 38        internal HttpRequestOptions? _options;
 39
 40        public Version Version
 41        {
 042            get { return _version; }
 43            set
 044            {
 045                ArgumentNullException.ThrowIfNull(value);
 046                CheckDisposed();
 47
 048                _version = value;
 049            }
 50        }
 51
 52        /// <summary>
 53        /// Gets or sets the policy determining how <see cref="Version" /> is interpreted and how is the final HTTP vers
 54        /// </summary>
 55        public HttpVersionPolicy VersionPolicy
 56        {
 057            get { return _versionPolicy; }
 58            set
 059            {
 060                if ((uint)value > (uint)HttpVersionPolicy.RequestVersionExact)
 061                {
 062                    throw new ArgumentException(SR.Format(SR.net_invalid_enum, nameof(HttpVersionPolicy)), nameof(value)
 63                }
 64
 065                CheckDisposed();
 66
 067                _versionPolicy = value;
 068            }
 69        }
 70
 71        public HttpContent? Content
 72        {
 073            get { return _content; }
 74            set
 075            {
 076                CheckDisposed();
 77
 078                if (NetEventSource.Log.IsEnabled())
 079                {
 080                    if (value == null)
 081                    {
 082                        NetEventSource.ContentNull(this);
 083                    }
 84                    else
 085                    {
 086                        NetEventSource.Associate(this, value);
 087                    }
 088                }
 89
 90                // It's OK to set a 'null' content, even if the method is POST/PUT.
 091                _content = value;
 092            }
 93        }
 94
 95        public HttpMethod Method
 96        {
 097            get { return _method; }
 98            set
 099            {
 0100                ArgumentNullException.ThrowIfNull(value);
 0101                CheckDisposed();
 102
 0103                _method = value;
 0104            }
 105        }
 106
 107        public Uri? RequestUri
 108        {
 0109            get { return _requestUri; }
 110            set
 0111            {
 0112                CheckDisposed();
 0113                _requestUri = value;
 0114            }
 115        }
 116
 1117        public HttpRequestHeaders Headers => _headers ??= new HttpRequestHeaders();
 118
 0119        internal bool HasHeaders => _headers != null;
 120
 121        [Obsolete("HttpRequestMessage.Properties has been deprecated. Use Options instead.")]
 0122        public IDictionary<string, object?> Properties => Options;
 123
 124        /// <summary>
 125        /// Gets the collection of options to configure the HTTP request.
 126        /// </summary>
 0127        public HttpRequestOptions Options => _options ??= new HttpRequestOptions();
 128
 129        /// <summary>
 130        /// Gets or sets the identifier of the connection that this request was most recently sent on. The value is not
 131        /// guaranteed to be set: it remains <see langword="null"/> when the request was not handled by a connection, fo
 132        /// example because it timed out before a connection could be obtained.
 133        /// </summary>
 134        /// <remarks>
 135        /// When the request is sent through a <see cref="SocketsHttpHandler"/>, the value matches the connection id
 136        /// reported through EventSource telemetry and the id passed to
 137        /// <see cref="SocketsHttpHandler.ShouldEvictConnection"/> for the connection that served the request, allowing
 138        /// a caller to correlate a request with that connection. It also matches the id surfaced to a custom
 139        /// <see cref="SocketsHttpHandler.ConnectCallback"/>. When a request is sent over multiple connections (for
 140        /// example after a redirect or a retry), the value reflects the most recent attempt.
 141        /// <para>
 142        /// HTTP CONNECT proxy tunnels are an exception to the correlation with a custom
 143        /// <see cref="SocketsHttpHandler.ConnectCallback"/>: when the request is served over such a tunnel, the callbac
 144        /// observes the tunnel's underlying transport connection to the proxy, whose id differs from this one (which
 145        /// identifies the tunneled connection that carried the request). Both ids remain observable through a
 146        /// <see cref="SocketsHttpHandler.PlaintextStreamFilter"/>, which runs once per hop and reports the transport
 147        /// connection's id for the CONNECT hop and this id for the tunneled hop.
 148        /// </para>
 149        /// <para>
 150        /// These correlations apply only when the request is handled by <see cref="SocketsHttpHandler"/>. Another
 151        /// <see cref="HttpMessageHandler"/> may never set this value, or may assign it a different meaning.
 152        /// </para>
 153        /// <para>
 154        /// This property is intended to be read after the request has been sent. Assigning a value before the request
 155        /// is sent has no effect on how the request is handled: it does not request or influence the use of a particula
 156        /// connection, and any value set by the caller is overwritten with the id of the connection that actually serve
 157        /// the request.
 158        /// </para>
 159        /// </remarks>
 160        [Experimental(Experimentals.SocketsHttpHandlerExperimentalDiagId, UrlFormat = Experimentals.SharedUrlFormat)]
 161        public long? ConnectionId
 162        {
 163            // ConnectionIdSet is stored separately to avoid the extra bytes needed for a nullable 'long?' field.
 0164            get => _flags.HasFlag(MessageFlags.ConnectionIdSet) ? _connectionId : null;
 165            set
 0166            {
 0167                if (value is null)
 0168                {
 0169                    _flags &= ~MessageFlags.ConnectionIdSet;
 0170                }
 171                else
 0172                {
 0173                    _connectionId = value.Value;
 0174                    _flags |= MessageFlags.ConnectionIdSet;
 0175                }
 0176            }
 177        }
 178
 179        public HttpRequestMessage()
 1180            : this(HttpMethod.Get, (Uri?)null)
 1181        {
 1182        }
 183
 1184        public HttpRequestMessage(HttpMethod method, Uri? requestUri)
 1185        {
 1186            ArgumentNullException.ThrowIfNull(method);
 187
 188            // It's OK to have a 'null' request Uri. If HttpClient is used, the 'BaseAddress' will be added.
 189            // If there is no 'BaseAddress', sending this request message will throw.
 190            // Note that we also allow the string to be empty: null and empty are considered equivalent.
 1191            _method = method;
 1192            _requestUri = requestUri;
 1193            _version = DefaultRequestVersion;
 1194            _versionPolicy = DefaultVersionPolicy;
 1195        }
 196
 197        public HttpRequestMessage(HttpMethod method, [StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri)
 0198            : this(method, string.IsNullOrEmpty(requestUri) ? null : new Uri(requestUri, UriKind.RelativeOrAbsolute))
 0199        {
 0200        }
 201
 202        public override string ToString()
 0203        {
 0204            ValueStringBuilder sb = new ValueStringBuilder(stackalloc char[512]);
 205
 0206            sb.Append("Method: ");
 0207            sb.Append(_method.ToString());
 208
 0209            sb.Append(", RequestUri: '");
 0210            if (_requestUri is null)
 0211            {
 0212                sb.Append("<null>");
 0213            }
 214            else
 0215            {
 0216                sb.AppendSpanFormattable(_requestUri);
 0217            }
 218
 0219            sb.Append("', Version: ");
 0220            sb.AppendSpanFormattable(_version);
 221
 0222            sb.Append(", Content: ");
 0223            sb.Append(_content == null ? "<null>" : _content.GetType().ToString());
 224
 0225            sb.Append(", Headers:");
 0226            sb.Append(Environment.NewLine);
 0227            HeaderUtilities.DumpHeaders(ref sb, _headers, _content?.Headers);
 228
 0229            return sb.ToString();
 0230        }
 231
 232        internal bool MarkAsSent()
 0233        {
 0234            MessageFlags previousFlags = _flags;
 0235            _flags = previousFlags | MessageFlags.AlreadySent;
 0236            return !previousFlags.HasFlag(MessageFlags.AlreadySent);
 0237        }
 238
 0239        internal bool WasSentByHttpClient() => _flags.HasFlag(MessageFlags.AlreadySent);
 240
 0241        internal void MarkPropagatorStateInjectedByDiagnosticsHandler() => _flags |= MessageFlags.PropagatorStateInjecte
 242
 0243        internal bool WasPropagatorStateInjectedByDiagnosticsHandler() => _flags.HasFlag(MessageFlags.PropagatorStateInj
 244
 0245        internal void DisableAuth() => _flags |= MessageFlags.AuthDisabled;
 246
 0247        internal bool IsAuthDisabled() => _flags.HasFlag(MessageFlags.AuthDisabled);
 248
 0249        internal bool IsConnectionPoolPartitioningBySniDisabled() => _flags.HasFlag(MessageFlags.DoNotPartitionConnectio
 250
 251        // Experimental opt-in accessed via UnsafeAccessor from System.Net.Http tests. There is no product code path
 252        // that sets this flag today, so it is preserved from the trimmer via ILLink.Descriptors.LibraryBuild.xml.
 0253        internal void ExperimentalDangerousDoNotPartitionConnectionPoolBySni() => _flags |= MessageFlags.DoNotPartitionC
 254
 255        private bool Disposed
 256        {
 0257            get => _flags.HasFlag(MessageFlags.Disposed);
 258            set
 0259            {
 0260                Debug.Assert(value);
 0261                _flags |= MessageFlags.Disposed;
 0262            }
 263        }
 264
 0265        internal bool IsExtendedConnectRequest => Method == HttpMethod.Connect && _headers?.Protocol != null;
 266
 267        #region IDisposable Members
 268
 269        protected virtual void Dispose(bool disposing)
 0270        {
 271            // The reason for this type to implement IDisposable is that it contains instances of types that implement
 272            // IDisposable (content).
 0273            if (disposing && !Disposed)
 0274            {
 0275                Disposed = true;
 0276                _content?.Dispose();
 0277            }
 0278        }
 279
 280        public void Dispose()
 0281        {
 0282            Dispose(true);
 0283            GC.SuppressFinalize(this);
 0284        }
 285
 286        #endregion
 287
 288        private void CheckDisposed()
 0289        {
 0290            ObjectDisposedException.ThrowIf(Disposed, this);
 0291        }
 292    }
 293}
 294