| File: System\Net\Http\HttpResponseMessage.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.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.ContainsNewLineOrNull(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() { ValueStringBuilder sb = new ValueStringBuilder(stackalloc char[512]); sb.Append("StatusCode: "); sb.AppendSpanFormattable((int)_statusCode); sb.Append(", ReasonPhrase: '"); sb.Append(ReasonPhrase ?? "<null>"); 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); if (_trailingHeaders != null) { sb.Append(", Trailing Headers:"); sb.Append(Environment.NewLine); HeaderUtilities.DumpHeaders(ref 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); } } }