File: System\Net\Http\HttpResponseMessage.cs
Web Access
Project: src\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.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Net.Http.Headers;
using System.Text;
 
namespace System.Net.Http
{
    public class HttpResponseMessage : IDisposable
    {
        private const HttpStatusCode DefaultStatusCode = HttpStatusCode.OK;
        private static Version DefaultResponseVersion => HttpVersion.Version11;
 
        private HttpStatusCode _statusCode;
        private HttpResponseHeaders? _headers;
        private HttpResponseHeaders? _trailingHeaders;
        private string? _reasonPhrase;
        private HttpRequestMessage? _requestMessage;
        private Version _version;
        private HttpContent? _content;
        private bool _disposed;
 
        public Version Version
        {
            get { return _version; }
            set
            {
#if !PHONE
                ArgumentNullException.ThrowIfNull(value);
#endif
                CheckDisposed();
 
                _version = value;
            }
        }
 
        internal void SetVersionWithoutValidation(Version value) => _version = value;
 
        [AllowNull]
        public HttpContent Content
        {
            get { return _content ??= new EmptyContent(); }
            set
            {
                CheckDisposed();
 
                if (NetEventSource.Log.IsEnabled())
                {
                    if (value == null)
                    {
                        NetEventSource.ContentNull(this);
                    }
                    else
                    {
                        NetEventSource.Associate(this, value);
                    }
                }
 
                _content = value;
            }
        }
 
        public HttpStatusCode StatusCode
        {
            get { return _statusCode; }
            set
            {
                ArgumentOutOfRangeException.ThrowIfNegative((int)value, nameof(value));
                ArgumentOutOfRangeException.ThrowIfGreaterThan((int)value, 999, nameof(value));
                CheckDisposed();
 
                _statusCode = value;
            }
        }
 
        internal void SetStatusCodeWithoutValidation(HttpStatusCode value) => _statusCode = value;
 
        public string? ReasonPhrase
        {
            get
            {
                if (_reasonPhrase != null)
                {
                    return _reasonPhrase;
                }
                // Provide a default if one was not set.
                return HttpStatusDescription.Get(StatusCode);
            }
            set
            {
                if ((value != null) && HttpRuleParser.ContainsNewLine(value))
                {
                    throw new FormatException(SR.net_http_reasonphrase_format_error);
                }
                CheckDisposed();
 
                _reasonPhrase = value; // It's OK to have a 'null' reason phrase.
            }
        }
 
        internal void SetReasonPhraseWithoutValidation(string value) => _reasonPhrase = value;
 
        public HttpResponseHeaders Headers => _headers ??= new HttpResponseHeaders();
 
        public HttpResponseHeaders TrailingHeaders => _trailingHeaders ??= new HttpResponseHeaders(containsTrailingHeaders: true);
 
        /// <summary>Stores the supplied trailing headers into this instance.</summary>
        /// <remarks>
        /// In the common/desired case where response.TrailingHeaders isn't accessed until after the whole payload has been
        /// received, <see cref="_trailingHeaders" /> will still be null, and we can simply store the supplied instance into
        /// <see cref="_trailingHeaders" /> and assume ownership of the instance.  In the uncommon case where it was accessed,
        /// we add all of the headers to the existing instance.
        /// </remarks>
        internal void StoreReceivedTrailingHeaders(HttpResponseHeaders headers)
        {
            Debug.Assert(headers.ContainsTrailingHeaders);
 
            if (_trailingHeaders is null)
            {
                _trailingHeaders = headers;
            }
            else
            {
                _trailingHeaders.AddHeaders(headers);
            }
        }
 
        public HttpRequestMessage? RequestMessage
        {
            get { return _requestMessage; }
            set
            {
                CheckDisposed();
                if (value is not null && NetEventSource.Log.IsEnabled())
                    NetEventSource.Associate(this, value);
                _requestMessage = value;
            }
        }
 
        public bool IsSuccessStatusCode
        {
            get { return ((int)_statusCode >= 200) && ((int)_statusCode <= 299); }
        }
 
        public HttpResponseMessage()
            : this(DefaultStatusCode)
        {
        }
 
        public HttpResponseMessage(HttpStatusCode statusCode)
        {
            ArgumentOutOfRangeException.ThrowIfNegative((int)statusCode, nameof(statusCode));
            ArgumentOutOfRangeException.ThrowIfGreaterThan((int)statusCode, 999, nameof(statusCode));
 
            _statusCode = statusCode;
            _version = DefaultResponseVersion;
        }
 
        public HttpResponseMessage EnsureSuccessStatusCode()
        {
            if (!IsSuccessStatusCode)
            {
                throw new HttpRequestException(
                    SR.Format(
                        System.Globalization.CultureInfo.InvariantCulture,
                        string.IsNullOrWhiteSpace(ReasonPhrase) ? SR.net_http_message_not_success_statuscode : SR.net_http_message_not_success_statuscode_reason,
                        (int)_statusCode,
                        ReasonPhrase),
                    inner: null,
                    _statusCode);
            }
 
            return this;
        }
 
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
 
            sb.Append("StatusCode: ");
            sb.Append((int)_statusCode);
 
            sb.Append(", ReasonPhrase: '");
            sb.Append(ReasonPhrase ?? "<null>");
 
            sb.Append("', Version: ");
            sb.Append(_version);
 
            sb.Append(", Content: ");
            sb.Append(_content == null ? "<null>" : _content.GetType().ToString());
 
            sb.AppendLine(", Headers:");
            HeaderUtilities.DumpHeaders(sb, _headers, _content?.Headers);
 
            if (_trailingHeaders != null)
            {
                sb.AppendLine(", Trailing Headers:");
                HeaderUtilities.DumpHeaders(sb, _trailingHeaders);
            }
 
            return sb.ToString();
        }
 
        #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);
        }
    }
}