jellyfin/SocketHttpListener/Net/HttpListenerRequest.cs
Erwin de Haan ec1f5dc317 Mayor code cleanup
Add Argument*Exceptions now use proper nameof operators.

Added exception messages to quite a few Argument*Exceptions.

Fixed rethorwing to be proper syntax.

Added a ton of null checkes. (This is only a start, there are about 500 places that need proper null handling)

Added some TODOs to log certain exceptions.

Fix sln again.

Fixed all AssemblyInfo's and added proper copyright (where I could find them)

We live in *current year*.

Fixed the use of braces.

Fixed a ton of properties, and made a fair amount of functions static that should be and can be static.

Made more Methods that should be static static.

You can now use static to find bad functions!

Removed unused variable. And added one more proper XML comment.
2019-01-10 20:38:53 +01:00

545 lines
17 KiB
C#

using System;
using System.Collections.Specialized;
using System.Globalization;
using System.IO;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.Text;
using SocketHttpListener.Primitives;
using System.Collections.Generic;
using SocketHttpListener.Net.WebSockets;
namespace SocketHttpListener.Net
{
public sealed unsafe partial class HttpListenerRequest
{
private CookieCollection _cookies;
private bool? _keepAlive;
private string _rawUrl;
private Uri _requestUri;
private Version _version;
public string[] AcceptTypes => Helpers.ParseMultivalueHeader(Headers[HttpKnownHeaderNames.Accept]);
public string[] UserLanguages => Helpers.ParseMultivalueHeader(Headers[HttpKnownHeaderNames.AcceptLanguage]);
private static CookieCollection ParseCookies(Uri uri, string setCookieHeader)
{
CookieCollection cookies = new CookieCollection();
return cookies;
}
public CookieCollection Cookies
{
get
{
if (_cookies == null)
{
string cookieString = Headers[HttpKnownHeaderNames.Cookie];
if (!string.IsNullOrEmpty(cookieString))
{
_cookies = ParseCookies(RequestUri, cookieString);
}
if (_cookies == null)
{
_cookies = new CookieCollection();
}
}
return _cookies;
}
}
public Encoding ContentEncoding
{
get
{
if (UserAgent != null && CultureInfo.InvariantCulture.CompareInfo.IsPrefix(UserAgent, "UP"))
{
string postDataCharset = Headers["x-up-devcap-post-charset"];
if (postDataCharset != null && postDataCharset.Length > 0)
{
try
{
return Encoding.GetEncoding(postDataCharset);
}
catch (ArgumentException)
{
}
}
}
if (HasEntityBody)
{
if (ContentType != null)
{
string charSet = Helpers.GetCharSetValueFromHeader(ContentType);
if (charSet != null)
{
try
{
return Encoding.GetEncoding(charSet);
}
catch (ArgumentException)
{
}
}
}
}
return TextEncodingExtensions.GetDefaultEncoding();
}
}
public string ContentType => Headers[HttpKnownHeaderNames.ContentType];
public bool IsLocal => LocalEndPoint.Address.Equals(RemoteEndPoint.Address);
public bool IsWebSocketRequest
{
get
{
if (!SupportsWebSockets)
{
return false;
}
bool foundConnectionUpgradeHeader = false;
if (string.IsNullOrEmpty(Headers[HttpKnownHeaderNames.Connection]) || string.IsNullOrEmpty(Headers[HttpKnownHeaderNames.Upgrade]))
{
return false;
}
foreach (string connection in Headers.GetValues(HttpKnownHeaderNames.Connection))
{
if (string.Equals(connection, HttpKnownHeaderNames.Upgrade, StringComparison.OrdinalIgnoreCase))
{
foundConnectionUpgradeHeader = true;
break;
}
}
if (!foundConnectionUpgradeHeader)
{
return false;
}
foreach (string upgrade in Headers.GetValues(HttpKnownHeaderNames.Upgrade))
{
if (string.Equals(upgrade, HttpWebSocket.WebSocketUpgradeToken, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
}
public bool KeepAlive
{
get
{
if (!_keepAlive.HasValue)
{
string header = Headers[HttpKnownHeaderNames.ProxyConnection];
if (string.IsNullOrEmpty(header))
{
header = Headers[HttpKnownHeaderNames.Connection];
}
if (string.IsNullOrEmpty(header))
{
if (ProtocolVersion >= HttpVersion.Version11)
{
_keepAlive = true;
}
else
{
header = Headers[HttpKnownHeaderNames.KeepAlive];
_keepAlive = !string.IsNullOrEmpty(header);
}
}
else
{
header = header.ToLower(CultureInfo.InvariantCulture);
_keepAlive =
header.IndexOf("close", StringComparison.OrdinalIgnoreCase) < 0 ||
header.IndexOf("keep-alive", StringComparison.OrdinalIgnoreCase) >= 0;
}
}
return _keepAlive.Value;
}
}
public QueryParamCollection QueryString
{
get
{
QueryParamCollection queryString = new QueryParamCollection();
Helpers.FillFromString(queryString, Url.Query, true, ContentEncoding);
return queryString;
}
}
public string RawUrl => _rawUrl;
private string RequestScheme => IsSecureConnection ? UriScheme.Https : UriScheme.Http;
public string UserAgent => Headers[HttpKnownHeaderNames.UserAgent];
public string UserHostAddress => LocalEndPoint.ToString();
public string UserHostName => Headers[HttpKnownHeaderNames.Host];
public Uri UrlReferrer
{
get
{
string referrer = Headers[HttpKnownHeaderNames.Referer];
if (referrer == null)
{
return null;
}
bool success = Uri.TryCreate(referrer, UriKind.RelativeOrAbsolute, out Uri urlReferrer);
return success ? urlReferrer : null;
}
}
public Uri Url => RequestUri;
public Version ProtocolVersion => _version;
private static class Helpers
{
//
// Get attribute off header value
//
internal static string GetCharSetValueFromHeader(string headerValue)
{
const string AttrName = "charset";
if (headerValue == null)
return null;
int l = headerValue.Length;
int k = AttrName.Length;
// find properly separated attribute name
int i = 1; // start searching from 1
while (i < l)
{
i = CultureInfo.InvariantCulture.CompareInfo.IndexOf(headerValue, AttrName, i, CompareOptions.IgnoreCase);
if (i < 0)
break;
if (i + k >= l)
break;
char chPrev = headerValue[i - 1];
char chNext = headerValue[i + k];
if ((chPrev == ';' || chPrev == ',' || char.IsWhiteSpace(chPrev)) && (chNext == '=' || char.IsWhiteSpace(chNext)))
break;
i += k;
}
if (i < 0 || i >= l)
return null;
// skip to '=' and the following whitespace
i += k;
while (i < l && char.IsWhiteSpace(headerValue[i]))
i++;
if (i >= l || headerValue[i] != '=')
return null;
i++;
while (i < l && char.IsWhiteSpace(headerValue[i]))
i++;
if (i >= l)
return null;
// parse the value
string attrValue = null;
int j;
if (i < l && headerValue[i] == '"')
{
if (i == l - 1)
return null;
j = headerValue.IndexOf('"', i + 1);
if (j < 0 || j == i + 1)
return null;
attrValue = headerValue.Substring(i + 1, j - i - 1).Trim();
}
else
{
for (j = i; j < l; j++)
{
if (headerValue[j] == ';')
break;
}
if (j == i)
return null;
attrValue = headerValue.Substring(i, j - i).Trim();
}
return attrValue;
}
internal static string[] ParseMultivalueHeader(string s)
{
if (s == null)
return null;
int l = s.Length;
// collect comma-separated values into list
List<string> values = new List<string>();
int i = 0;
while (i < l)
{
// find next ,
int ci = s.IndexOf(',', i);
if (ci < 0)
ci = l;
// append corresponding server value
values.Add(s.Substring(i, ci - i));
// move to next
i = ci + 1;
// skip leading space
if (i < l && s[i] == ' ')
i++;
}
// return list as array of strings
int n = values.Count;
string[] strings;
// if n is 0 that means s was empty string
if (n == 0)
{
strings = new string[1];
strings[0] = string.Empty;
}
else
{
strings = new string[n];
values.CopyTo(0, strings, 0, n);
}
return strings;
}
private static string UrlDecodeStringFromStringInternal(string s, Encoding e)
{
int count = s.Length;
UrlDecoder helper = new UrlDecoder(count, e);
// go through the string's chars collapsing %XX and %uXXXX and
// appending each char as char, with exception of %XX constructs
// that are appended as bytes
for (int pos = 0; pos < count; pos++)
{
char ch = s[pos];
if (ch == '+')
{
ch = ' ';
}
else if (ch == '%' && pos < count - 2)
{
if (s[pos + 1] == 'u' && pos < count - 5)
{
int h1 = HexToInt(s[pos + 2]);
int h2 = HexToInt(s[pos + 3]);
int h3 = HexToInt(s[pos + 4]);
int h4 = HexToInt(s[pos + 5]);
if (h1 >= 0 && h2 >= 0 && h3 >= 0 && h4 >= 0)
{ // valid 4 hex chars
ch = (char)((h1 << 12) | (h2 << 8) | (h3 << 4) | h4);
pos += 5;
// only add as char
helper.AddChar(ch);
continue;
}
}
else
{
int h1 = HexToInt(s[pos + 1]);
int h2 = HexToInt(s[pos + 2]);
if (h1 >= 0 && h2 >= 0)
{ // valid 2 hex chars
byte b = (byte)((h1 << 4) | h2);
pos += 2;
// don't add as char
helper.AddByte(b);
continue;
}
}
}
if ((ch & 0xFF80) == 0)
helper.AddByte((byte)ch); // 7 bit have to go as bytes because of Unicode
else
helper.AddChar(ch);
}
return helper.GetString();
}
private static int HexToInt(char h)
{
return (h >= '0' && h <= '9') ? h - '0' :
(h >= 'a' && h <= 'f') ? h - 'a' + 10 :
(h >= 'A' && h <= 'F') ? h - 'A' + 10 :
-1;
}
private class UrlDecoder
{
private int _bufferSize;
// Accumulate characters in a special array
private int _numChars;
private char[] _charBuffer;
// Accumulate bytes for decoding into characters in a special array
private int _numBytes;
private byte[] _byteBuffer;
// Encoding to convert chars to bytes
private Encoding _encoding;
private void FlushBytes()
{
if (_numBytes > 0)
{
_numChars += _encoding.GetChars(_byteBuffer, 0, _numBytes, _charBuffer, _numChars);
_numBytes = 0;
}
}
internal UrlDecoder(int bufferSize, Encoding encoding)
{
_bufferSize = bufferSize;
_encoding = encoding;
_charBuffer = new char[bufferSize];
// byte buffer created on demand
}
internal void AddChar(char ch)
{
if (_numBytes > 0)
FlushBytes();
_charBuffer[_numChars++] = ch;
}
internal void AddByte(byte b)
{
{
if (_byteBuffer == null)
_byteBuffer = new byte[_bufferSize];
_byteBuffer[_numBytes++] = b;
}
}
internal string GetString()
{
if (_numBytes > 0)
FlushBytes();
if (_numChars > 0)
return new string(_charBuffer, 0, _numChars);
else
return string.Empty;
}
}
internal static void FillFromString(QueryParamCollection nvc, string s, bool urlencoded, Encoding encoding)
{
int l = (s != null) ? s.Length : 0;
int i = (s.Length > 0 && s[0] == '?') ? 1 : 0;
while (i < l)
{
// find next & while noting first = on the way (and if there are more)
int si = i;
int ti = -1;
while (i < l)
{
char ch = s[i];
if (ch == '=')
{
if (ti < 0)
ti = i;
}
else if (ch == '&')
{
break;
}
i++;
}
// extract the name / value pair
string name = null;
string value = null;
if (ti >= 0)
{
name = s.Substring(si, ti - si);
value = s.Substring(ti + 1, i - ti - 1);
}
else
{
value = s.Substring(si, i - si);
}
// add name / value pair to the collection
if (urlencoded)
nvc.Add(
name == null ? null : UrlDecodeStringFromStringInternal(name, encoding),
UrlDecodeStringFromStringInternal(value, encoding));
else
nvc.Add(name, value);
// trailing '&'
if (i == l - 1 && s[i] == '&')
nvc.Add(null, "");
i++;
}
}
}
}
}