| File: System\Net\Http\HttpRequestMessage.cs | Web Access |
| Project: src\runtime\src\libraries\System.Net.Http\src\System.Net.Http.csproj (System.Net.Http) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Net.Http.Headers; using System.Text; using System.Threading; namespace System.Net.Http { public class HttpRequestMessage : IDisposable { internal static Version DefaultRequestVersion => HttpVersion.Version11; internal static HttpVersionPolicy DefaultVersionPolicy => HttpVersionPolicy.RequestVersionOrLower; private const int MessageNotYetSent = 0; private const int MessageAlreadySent = 1; private const int PropagatorStateInjectedByDiagnosticsHandler = 2; private const int MessageDisposed = 4; private const int AuthDisabled = 8; // Track whether the message has been sent. // The message shouldn't be sent again if this field is equal to MessageAlreadySent. private int _sendStatus = MessageNotYetSent; private HttpMethod _method; private Uri? _requestUri; private HttpRequestHeaders? _headers; private Version _version; private HttpVersionPolicy _versionPolicy; private HttpContent? _content; internal HttpRequestOptions? _options; public Version Version { get { return _version; } set { ArgumentNullException.ThrowIfNull(value); CheckDisposed(); _version = value; } } /// <summary> /// Gets or sets the policy determining how <see cref="Version" /> is interpreted and how is the final HTTP version negotiated with the server. /// </summary> public HttpVersionPolicy VersionPolicy { get { return _versionPolicy; } set { if ((uint)value > (uint)HttpVersionPolicy.RequestVersionExact) { throw new ArgumentException(SR.Format(SR.net_invalid_enum, nameof(HttpVersionPolicy)), nameof(value)); } CheckDisposed(); _versionPolicy = value; } } public HttpContent? Content { get { return _content; } set { CheckDisposed(); if (NetEventSource.Log.IsEnabled()) { if (value == null) { NetEventSource.ContentNull(this); } else { NetEventSource.Associate(this, value); } } // It's OK to set a 'null' content, even if the method is POST/PUT. _content = value; } } public HttpMethod Method { get { return _method; } set { ArgumentNullException.ThrowIfNull(value); CheckDisposed(); _method = value; } } public Uri? RequestUri { get { return _requestUri; } set { CheckDisposed(); _requestUri = value; } } public HttpRequestHeaders Headers => _headers ??= new HttpRequestHeaders(); internal bool HasHeaders => _headers != null; [Obsolete("HttpRequestMessage.Properties has been deprecated. Use Options instead.")] public IDictionary<string, object?> Properties => Options; /// <summary> /// Gets the collection of options to configure the HTTP request. /// </summary> public HttpRequestOptions Options => _options ??= new HttpRequestOptions(); public HttpRequestMessage() : this(HttpMethod.Get, (Uri?)null) { } public HttpRequestMessage(HttpMethod method, Uri? requestUri) { ArgumentNullException.ThrowIfNull(method); // It's OK to have a 'null' request Uri. If HttpClient is used, the 'BaseAddress' will be added. // If there is no 'BaseAddress', sending this request message will throw. // Note that we also allow the string to be empty: null and empty are considered equivalent. _method = method; _requestUri = requestUri; _version = DefaultRequestVersion; _versionPolicy = DefaultVersionPolicy; } public HttpRequestMessage(HttpMethod method, [StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri) : this(method, string.IsNullOrEmpty(requestUri) ? null : new Uri(requestUri, UriKind.RelativeOrAbsolute)) { } public override string ToString() { ValueStringBuilder sb = new ValueStringBuilder(stackalloc char[512]); sb.Append("Method: "); sb.Append(_method.ToString()); sb.Append(", RequestUri: '"); if (_requestUri is null) { sb.Append("<null>"); } else { sb.AppendSpanFormattable(_requestUri); } sb.Append("', Version: "); sb.AppendSpanFormattable(_version); sb.Append(", Content: "); sb.Append(_content == null ? "<null>" : _content.GetType().ToString()); sb.Append(", Headers:"); sb.Append(Environment.NewLine); HeaderUtilities.DumpHeaders(ref sb, _headers, _content?.Headers); return sb.ToString(); } internal bool MarkAsSent() => Interlocked.CompareExchange(ref _sendStatus, MessageAlreadySent, MessageNotYetSent) == MessageNotYetSent; internal bool WasSentByHttpClient() => (_sendStatus & MessageAlreadySent) != 0; internal void MarkPropagatorStateInjectedByDiagnosticsHandler() => _sendStatus |= PropagatorStateInjectedByDiagnosticsHandler; internal bool WasPropagatorStateInjectedByDiagnosticsHandler() => (_sendStatus & PropagatorStateInjectedByDiagnosticsHandler) != 0; internal void DisableAuth() => _sendStatus |= AuthDisabled; internal bool IsAuthDisabled() => (_sendStatus & AuthDisabled) != 0; private bool Disposed { get => (_sendStatus & MessageDisposed) != 0; set { Debug.Assert(value); _sendStatus |= MessageDisposed; } } internal bool IsExtendedConnectRequest => Method == HttpMethod.Connect && _headers?.Protocol != null; #region IDisposable Members protected virtual void Dispose(bool disposing) { // The reason for this type to implement IDisposable is that it contains instances of types that implement // IDisposable (content). if (disposing && !Disposed) { Disposed = true; _content?.Dispose(); } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } #endregion private void CheckDisposed() { ObjectDisposedException.ThrowIf(Disposed, this); } } }