jellyfin-server/SocketHttpListener/Net/HttpListenerResponse.cs

305 lines
9.2 KiB
C#
Raw Normal View History

2017-06-15 17:22:05 +00:00
using System;
using System.Collections.Generic;
2016-11-11 19:55:12 +00:00
using System.IO;
using System.Net;
using System.Text;
2017-03-12 19:27:26 +00:00
using System.Threading.Tasks;
2017-06-15 17:22:05 +00:00
using System.Globalization;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Diagnostics;
using Microsoft.Win32.SafeHandles;
2016-11-11 19:55:12 +00:00
namespace SocketHttpListener.Net
{
2017-06-15 17:22:05 +00:00
public sealed unsafe partial class HttpListenerResponse : IDisposable
2016-11-11 19:55:12 +00:00
{
2017-06-15 17:22:05 +00:00
private BoundaryType _boundaryType = BoundaryType.None;
private CookieCollection _cookies;
private HttpListenerContext _httpContext;
private bool _keepAlive = true;
private HttpResponseStream _responseStream;
private string _statusDescription;
private WebHeaderCollection _webHeaders = new WebHeaderCollection();
2017-05-25 13:00:14 +00:00
2017-06-15 17:22:05 +00:00
public WebHeaderCollection Headers
2016-11-11 19:55:12 +00:00
{
2017-06-15 17:40:38 +00:00
get { return _webHeaders; }
2016-11-11 19:55:12 +00:00
}
2017-06-15 17:22:05 +00:00
public Encoding ContentEncoding { get; set; }
2016-11-11 19:55:12 +00:00
public string ContentType
{
2017-06-15 17:40:38 +00:00
get { return Headers["Content-Type"]; }
2016-11-11 19:55:12 +00:00
set
{
2017-06-15 17:22:05 +00:00
CheckDisposed();
if (string.IsNullOrEmpty(value))
{
Headers.Remove("Content-Type");
}
else
{
Headers.Set("Content-Type", value);
}
2016-11-11 19:55:12 +00:00
}
}
2017-06-15 17:40:38 +00:00
private HttpListenerContext HttpListenerContext { get { return _httpContext; } }
2016-11-11 19:55:12 +00:00
2017-06-15 17:40:38 +00:00
private HttpListenerRequest HttpListenerRequest { get { return HttpListenerContext.Request; } }
2016-11-11 19:55:12 +00:00
2017-06-15 17:22:05 +00:00
internal EntitySendFormat EntitySendFormat
2016-11-11 19:55:12 +00:00
{
2017-06-15 17:40:38 +00:00
get { return (EntitySendFormat)_boundaryType; }
2016-11-11 19:55:12 +00:00
set
{
2017-06-15 17:22:05 +00:00
CheckDisposed();
CheckSentHeaders();
if (value == EntitySendFormat.Chunked && HttpListenerRequest.ProtocolVersion.Minor == 0)
{
throw new ProtocolViolationException("net_nochunkuploadonhttp10");
}
_boundaryType = (BoundaryType)value;
if (value != EntitySendFormat.ContentLength)
{
_contentLength = -1;
}
2016-11-11 19:55:12 +00:00
}
}
2017-06-15 17:22:05 +00:00
public bool SendChunked
2016-11-11 19:55:12 +00:00
{
2017-06-15 17:40:38 +00:00
get { return EntitySendFormat == EntitySendFormat.Chunked; ; }
set { EntitySendFormat = value ? EntitySendFormat.Chunked : EntitySendFormat.ContentLength; }
2016-11-11 19:55:12 +00:00
}
2017-06-15 17:22:05 +00:00
// We MUST NOT send message-body when we send responses with these Status codes
private static readonly int[] s_noResponseBody = { 100, 101, 204, 205, 304 };
private static bool CanSendResponseBody(int responseCode)
2016-11-11 19:55:12 +00:00
{
2017-06-15 17:22:05 +00:00
for (int i = 0; i < s_noResponseBody.Length; i++)
2016-11-11 19:55:12 +00:00
{
2017-06-15 17:22:05 +00:00
if (responseCode == s_noResponseBody[i])
{
return false;
}
2016-11-11 19:55:12 +00:00
}
2017-06-15 17:22:05 +00:00
return true;
2016-11-11 19:55:12 +00:00
}
2017-06-15 17:22:05 +00:00
public long ContentLength64
2016-11-11 19:55:12 +00:00
{
2017-06-15 17:40:38 +00:00
get { return _contentLength; }
2016-11-11 19:55:12 +00:00
set
{
2017-06-15 17:22:05 +00:00
CheckDisposed();
CheckSentHeaders();
if (value >= 0)
{
_contentLength = value;
_boundaryType = BoundaryType.ContentLength;
}
else
{
throw new ArgumentOutOfRangeException("net_clsmall");
}
2016-11-11 19:55:12 +00:00
}
}
2017-06-15 17:22:05 +00:00
public CookieCollection Cookies
2016-11-11 19:55:12 +00:00
{
2017-06-15 17:40:38 +00:00
get { return _cookies ?? (_cookies = new CookieCollection()); }
set { _cookies = value; }
2016-11-11 19:55:12 +00:00
}
2017-06-15 17:22:05 +00:00
public bool KeepAlive
2016-11-11 19:55:12 +00:00
{
2017-06-15 17:40:38 +00:00
get { return _keepAlive; }
2016-11-11 19:55:12 +00:00
set
{
2017-06-15 17:22:05 +00:00
CheckDisposed();
_keepAlive = value;
2016-11-11 19:55:12 +00:00
}
}
2017-06-15 17:22:05 +00:00
public Stream OutputStream
2016-11-11 19:55:12 +00:00
{
2017-06-15 17:22:05 +00:00
get
2016-11-11 19:55:12 +00:00
{
2017-06-15 17:22:05 +00:00
CheckDisposed();
EnsureResponseStream();
return _responseStream;
2016-11-11 19:55:12 +00:00
}
}
2017-06-15 17:22:05 +00:00
public string RedirectLocation
2016-11-11 19:55:12 +00:00
{
2017-06-15 17:40:38 +00:00
get { return Headers["Location"]; }
2016-11-11 19:55:12 +00:00
set
{
2017-06-15 17:22:05 +00:00
// note that this doesn't set the status code to a redirect one
CheckDisposed();
if (string.IsNullOrEmpty(value))
{
Headers.Remove("Location");
}
else
{
Headers.Set("Location", value);
}
2016-11-11 19:55:12 +00:00
}
}
2017-06-15 17:22:05 +00:00
public string StatusDescription
2017-05-25 13:00:14 +00:00
{
2017-06-15 17:22:05 +00:00
get
2017-05-25 13:00:14 +00:00
{
2017-06-15 17:22:05 +00:00
if (_statusDescription == null)
2017-05-25 13:00:14 +00:00
{
2017-06-15 17:22:05 +00:00
// if the user hasn't set this, generated on the fly, if possible.
// We know this one is safe, no need to verify it as in the setter.
_statusDescription = HttpStatusDescription.Get(StatusCode);
2017-05-25 13:00:14 +00:00
}
2017-06-15 17:22:05 +00:00
if (_statusDescription == null)
2017-05-25 13:00:14 +00:00
{
2017-06-15 17:22:05 +00:00
_statusDescription = string.Empty;
2017-05-25 13:00:14 +00:00
}
2017-06-15 17:22:05 +00:00
return _statusDescription;
2017-05-25 13:00:14 +00:00
}
2017-06-15 17:22:05 +00:00
set
2017-05-25 13:00:14 +00:00
{
2017-06-15 17:22:05 +00:00
CheckDisposed();
if (value == null)
2017-05-25 13:00:14 +00:00
{
2017-06-15 17:22:05 +00:00
throw new ArgumentNullException(nameof(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)
2017-05-25 13:00:14 +00:00
{
2017-06-15 17:22:05 +00:00
throw new ArgumentException("net_WebHeaderInvalidControlChars");
2017-05-25 13:00:14 +00:00
}
2017-06-15 17:22:05 +00:00
}
_statusDescription = value;
2017-05-25 13:00:14 +00:00
}
}
2017-06-15 17:22:05 +00:00
public void AddHeader(string name, string value)
2016-11-11 19:55:12 +00:00
{
2017-06-15 17:22:05 +00:00
Headers.Set(name, value);
2016-11-11 19:55:12 +00:00
}
2017-06-15 17:22:05 +00:00
public void AppendHeader(string name, string value)
2016-11-11 19:55:12 +00:00
{
2017-06-15 17:22:05 +00:00
Headers.Add(name, value);
2016-11-11 19:55:12 +00:00
}
2017-06-15 17:22:05 +00:00
public void AppendCookie(Cookie cookie)
2016-11-11 19:55:12 +00:00
{
2017-06-15 17:22:05 +00:00
if (cookie == null)
2016-11-11 19:55:12 +00:00
{
2017-06-15 17:22:05 +00:00
throw new ArgumentNullException(nameof(cookie));
2016-11-11 19:55:12 +00:00
}
2017-06-15 17:22:05 +00:00
Cookies.Add(cookie);
2016-11-11 19:55:12 +00:00
}
2017-06-15 17:22:05 +00:00
private void ComputeCookies()
2016-11-28 05:38:41 +00:00
{
2017-06-15 17:22:05 +00:00
if (_cookies != null)
2016-11-28 05:38:41 +00:00
{
2017-06-15 17:22:05 +00:00
// 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 (cookieString == null || cookieString.Length == 0)
// {
// continue;
// }
// 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);
// }
//}
2016-11-28 05:38:41 +00:00
}
}
2017-06-15 17:22:05 +00:00
public void Redirect(string url)
2016-11-11 19:55:12 +00:00
{
2017-06-15 17:22:05 +00:00
Headers["Location"] = url;
StatusCode = (int)HttpStatusCode.Redirect;
StatusDescription = "Found";
}
2016-11-11 19:55:12 +00:00
2017-06-15 17:22:05 +00:00
public void SetCookie(Cookie cookie)
{
if (cookie == null)
2016-11-11 19:55:12 +00:00
{
2017-06-15 17:22:05 +00:00
throw new ArgumentNullException(nameof(cookie));
2016-11-11 19:55:12 +00:00
}
2017-06-15 17:22:05 +00:00
//Cookie newCookie = cookie.Clone();
//int added = Cookies.InternalAdd(newCookie, true);
2016-11-11 19:55:12 +00:00
2017-06-15 17:22:05 +00:00
//if (added != 1)
2016-11-11 19:55:12 +00:00
//{
2017-06-15 17:22:05 +00:00
// // The Cookie already existed and couldn't be replaced.
// throw new ArgumentException("Cookie exists");
2016-11-11 19:55:12 +00:00
//}
2017-06-15 17:22:05 +00:00
}
2016-11-11 19:55:12 +00:00
2017-06-15 17:40:38 +00:00
void IDisposable.Dispose()
{
Dispose();
}
2017-05-05 17:55:38 +00:00
2017-06-15 17:22:05 +00:00
private void CheckDisposed()
{
if (Disposed)
2016-11-11 19:55:12 +00:00
{
2017-06-15 17:22:05 +00:00
throw new ObjectDisposedException(GetType().FullName);
2016-11-11 19:55:12 +00:00
}
}
2017-06-15 17:22:05 +00:00
private void CheckSentHeaders()
2016-11-11 19:55:12 +00:00
{
2017-06-15 17:22:05 +00:00
if (SentHeaders)
2016-11-11 19:55:12 +00:00
{
2017-06-15 17:22:05 +00:00
throw new InvalidOperationException();
2016-11-11 19:55:12 +00:00
}
2017-03-12 19:27:26 +00:00
}
2016-11-11 19:55:12 +00:00
}
2017-06-15 17:22:05 +00:00
}