| File: System\Net\HttpListenerResponse.cs | Web Access |
| Project: src\runtime\src\libraries\System.Net.HttpListener\src\System.Net.HttpListener.csproj (System.Net.HttpListener) |
// 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.IO; using System.Text; namespace System.Net { public sealed partial class HttpListenerResponse : IDisposable { private BoundaryType _boundaryType = BoundaryType.None; private CookieCollection? _cookies; private readonly HttpListenerContext? _httpContext; private bool _keepAlive = true; private HttpResponseStream? _responseStream; private string? _statusDescription; private WebHeaderCollection _webHeaders = new WebHeaderCollection(); public WebHeaderCollection Headers { get => _webHeaders; set { _webHeaders = new WebHeaderCollection(); foreach (string headerName in value.AllKeys) { _webHeaders.Add(headerName, value[headerName]); } } } public Encoding? ContentEncoding { get; set; } public string? ContentType { get => Headers[HttpKnownHeaderNames.ContentType]; set { CheckDisposed(); if (string.IsNullOrEmpty(value)) { Headers.Remove(HttpKnownHeaderNames.ContentType); } else { Headers.Set(HttpKnownHeaderNames.ContentType, value); } } } private HttpListenerContext HttpListenerContext => _httpContext!; private HttpListenerRequest HttpListenerRequest => HttpListenerContext!.Request; internal EntitySendFormat EntitySendFormat { get => (EntitySendFormat)_boundaryType; set { CheckDisposed(); CheckSentHeaders(); if (value == EntitySendFormat.Chunked && HttpListenerRequest.ProtocolVersion.Minor == 0) { throw new ProtocolViolationException(SR.net_nochunkuploadonhttp10); } _boundaryType = (BoundaryType)value; if (value != EntitySendFormat.ContentLength) { _contentLength = -1; } } } public bool SendChunked { get => EntitySendFormat == EntitySendFormat.Chunked; set => EntitySendFormat = value ? EntitySendFormat.Chunked : EntitySendFormat.ContentLength; } private static bool CanSendResponseBody(int responseCode) => // We MUST NOT send message-body when we send responses with these Status codes responseCode is not (100 or 101 or 204 or 205 or 304); public long ContentLength64 { get => _contentLength; set { CheckDisposed(); CheckSentHeaders(); ArgumentOutOfRangeException.ThrowIfNegative(value); _contentLength = value; _boundaryType = BoundaryType.ContentLength; } } public CookieCollection Cookies { get => _cookies ??= new CookieCollection(); set => _cookies = value; } public bool KeepAlive { get => _keepAlive; set { CheckDisposed(); _keepAlive = value; } } public Stream OutputStream { get { CheckDisposed(); EnsureResponseStream(); return _responseStream!; } } public string? RedirectLocation { get => Headers[HttpResponseHeader.Location]; set { // note that this doesn't set the status code to a redirect one CheckDisposed(); if (string.IsNullOrEmpty(value)) { Headers.Remove(HttpKnownHeaderNames.Location); } else { Headers.Set(HttpKnownHeaderNames.Location, value); } } } public string StatusDescription { get { // if the user hasn't set this, generate on the fly, if possible. // We know this one is safe, no need to verify it as in the setter. return _statusDescription ??= HttpStatusDescription.Get(StatusCode) ?? string.Empty; } set { CheckDisposed(); ArgumentNullException.ThrowIfNull(value); // Need to verify the status description doesn't contain any control characters except HT. We mask off the high // byte since that's how it's encoded. for (int i = 0; i < value.Length; i++) { char c = (char)(0x000000ff & (uint)value[i]); if ((c <= 31 && c != (byte)'\t') || c == 127) { throw new ArgumentException(SR.net_WebHeaderInvalidControlChars, nameof(value)); } } _statusDescription = value; } } public void AddHeader(string name, string value) { if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"name={name}, value={value}"); Headers.Set(name, value); } public void AppendHeader(string name, string value) { if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"name={name}, value={value}"); Headers.Add(name, value); } public void AppendCookie(Cookie cookie) { ArgumentNullException.ThrowIfNull(cookie); if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"cookie: {cookie}"); Cookies.Add(cookie); } private void ComputeCookies() { if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"Entering Set-Cookie: {Headers[HttpResponseHeader.SetCookie]}, Set-Cookie2: {Headers[HttpKnownHeaderNames.SetCookie2]}"); if (_cookies != null) { // now go through the collection, and concatenate all the cookies in per-variant strings string? setCookie2 = null, setCookie = null; for (int index = 0; index < _cookies.Count; index++) { Cookie cookie = _cookies[index]; string cookieString = cookie.ToServerString(); if (string.IsNullOrEmpty(cookieString)) { continue; } if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"Now looking at index:{index} cookie: {cookie}"); if (cookie.IsRfc2965Variant()) { setCookie2 = setCookie2 == null ? cookieString : setCookie2 + ", " + cookieString; } else { setCookie = setCookie == null ? cookieString : setCookie + ", " + cookieString; } } if (!string.IsNullOrEmpty(setCookie)) { Headers.Set(HttpKnownHeaderNames.SetCookie, setCookie); if (string.IsNullOrEmpty(setCookie2)) { Headers.Remove(HttpKnownHeaderNames.SetCookie2); } } if (!string.IsNullOrEmpty(setCookie2)) { Headers.Set(HttpKnownHeaderNames.SetCookie2, setCookie2); if (string.IsNullOrEmpty(setCookie)) { Headers.Remove(HttpKnownHeaderNames.SetCookie); } } } if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"Exiting Set-Cookie: {Headers[HttpResponseHeader.SetCookie]} Set-Cookie2: {Headers[HttpKnownHeaderNames.SetCookie2]}"); } public void Redirect([StringSyntax(StringSyntaxAttribute.Uri)] string url) { if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"url={url}"); Headers[HttpResponseHeader.Location] = url; StatusCode = (int)HttpStatusCode.Redirect; StatusDescription = HttpStatusDescription.Get(StatusCode)!; } public void SetCookie(Cookie cookie) { ArgumentNullException.ThrowIfNull(cookie); Cookie newCookie = cookie.Clone(); int added = Cookies.InternalAdd(newCookie, true); if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"cookie: {cookie}"); if (added != 1) { // The Cookie already existed and couldn't be replaced. throw new ArgumentException(SR.net_cookie_exists, nameof(cookie)); } } void IDisposable.Dispose() => Dispose(); private void CheckDisposed() { ObjectDisposedException.ThrowIf(Disposed, this); } private void CheckSentHeaders() { if (SentHeaders) { throw new InvalidOperationException(SR.net_rspsubmitted); } } } }