aboutsummaryrefslogtreecommitdiff
path: root/SocketHttpListener/Net/HttpListenerRequest.cs
diff options
context:
space:
mode:
Diffstat (limited to 'SocketHttpListener/Net/HttpListenerRequest.cs')
-rw-r--r--SocketHttpListener/Net/HttpListenerRequest.cs937
1 files changed, 409 insertions, 528 deletions
diff --git a/SocketHttpListener/Net/HttpListenerRequest.cs b/SocketHttpListener/Net/HttpListenerRequest.cs
index 5e391424f..1b369dfa8 100644
--- a/SocketHttpListener/Net/HttpListenerRequest.cs
+++ b/SocketHttpListener/Net/HttpListenerRequest.cs
@@ -10,653 +10,534 @@ 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 class HttpListenerRequest
+ public sealed unsafe partial class HttpListenerRequest
{
- string[] accept_types;
- Encoding content_encoding;
- long content_length;
- bool cl_set;
- CookieCollection cookies;
- WebHeaderCollection headers;
- string method;
- Stream input_stream;
- Version version;
- QueryParamCollection query_string; // check if null is ok, check if read-only, check case-sensitiveness
- string raw_url;
- Uri url;
- Uri referrer;
- string[] user_languages;
- HttpListenerContext context;
- bool is_chunked;
- bool ka_set;
- bool? _keepAlive;
-
- private readonly ITextEncoding _textEncoding;
-
- internal HttpListenerRequest(HttpListenerContext context, ITextEncoding textEncoding)
- {
- this.context = context;
- _textEncoding = textEncoding;
- headers = new WebHeaderCollection();
- version = HttpVersion.Version10;
- }
+ private CookieCollection _cookies;
+ private bool? _keepAlive;
+ private string _rawUrl;
+ private Uri _requestUri;
+ private Version _version;
- static char[] separators = new char[] { ' ' };
+ public string[] AcceptTypes => Helpers.ParseMultivalueHeader(Headers[HttpKnownHeaderNames.Accept]);
- internal void SetRequestLine(string req)
- {
- string[] parts = req.Split(separators, 3);
- if (parts.Length != 3)
- {
- context.ErrorMessage = "Invalid request line (parts).";
- return;
- }
+ public string[] UserLanguages => Helpers.ParseMultivalueHeader(Headers[HttpKnownHeaderNames.AcceptLanguage]);
- method = parts[0];
- foreach (char c in method)
- {
- int ic = (int)c;
-
- if ((ic >= 'A' && ic <= 'Z') ||
- (ic > 32 && c < 127 && c != '(' && c != ')' && c != '<' &&
- c != '<' && c != '>' && c != '@' && c != ',' && c != ';' &&
- c != ':' && c != '\\' && c != '"' && c != '/' && c != '[' &&
- c != ']' && c != '?' && c != '=' && c != '{' && c != '}'))
- continue;
-
- context.ErrorMessage = "(Invalid verb)";
- return;
- }
-
- raw_url = parts[1];
- if (parts[2].Length != 8 || !parts[2].StartsWith("HTTP/"))
- {
- context.ErrorMessage = "Invalid request line (version).";
- return;
- }
-
- try
- {
- version = new Version(parts[2].Substring(5));
- if (version.Major < 1)
- throw new Exception();
- }
- catch
- {
- context.ErrorMessage = "Invalid request line (version).";
- return;
- }
+ private CookieCollection ParseCookies(Uri uri, string setCookieHeader)
+ {
+ CookieCollection cookies = new CookieCollection();
+ return cookies;
}
- void CreateQueryString(string query)
+ public CookieCollection Cookies
{
- if (query == null || query.Length == 0)
+ get
{
- query_string = new QueryParamCollection();
- return;
+ if (_cookies == null)
+ {
+ string cookieString = Headers[HttpKnownHeaderNames.Cookie];
+ if (!string.IsNullOrEmpty(cookieString))
+ {
+ _cookies = ParseCookies(RequestUri, cookieString);
+ }
+ if (_cookies == null)
+ {
+ _cookies = new CookieCollection();
+ }
+ }
+ return _cookies;
}
+ }
- query_string = new QueryParamCollection();
- if (query[0] == '?')
- query = query.Substring(1);
- string[] components = query.Split('&');
- foreach (string kv in components)
+ public Encoding ContentEncoding
+ {
+ get
{
- int pos = kv.IndexOf('=');
- if (pos == -1)
+ if (UserAgent != null && CultureInfo.InvariantCulture.CompareInfo.IsPrefix(UserAgent, "UP"))
{
- query_string.Add(null, WebUtility.UrlDecode(kv));
+ string postDataCharset = Headers["x-up-devcap-post-charset"];
+ if (postDataCharset != null && postDataCharset.Length > 0)
+ {
+ try
+ {
+ return Encoding.GetEncoding(postDataCharset);
+ }
+ catch (ArgumentException)
+ {
+ }
+ }
}
- else
+ if (HasEntityBody)
{
- string key = WebUtility.UrlDecode(kv.Substring(0, pos));
- string val = WebUtility.UrlDecode(kv.Substring(pos + 1));
-
- query_string.Add(key, val);
+ if (ContentType != null)
+ {
+ string charSet = Helpers.GetCharSetValueFromHeader(ContentType);
+ if (charSet != null)
+ {
+ try
+ {
+ return Encoding.GetEncoding(charSet);
+ }
+ catch (ArgumentException)
+ {
+ }
+ }
+ }
}
+ return TextEncodingExtensions.GetDefaultEncoding();
}
}
- internal void FinishInitialization()
- {
- string host = UserHostName;
- if (version > HttpVersion.Version10 && (host == null || host.Length == 0))
- {
- context.ErrorMessage = "Invalid host name";
- return;
- }
-
- string path;
- Uri raw_uri = null;
- if (MaybeUri(raw_url.ToLowerInvariant()) && Uri.TryCreate(raw_url, UriKind.Absolute, out raw_uri))
- path = raw_uri.PathAndQuery;
- else
- path = raw_url;
-
- if ((host == null || host.Length == 0))
- host = UserHostAddress;
-
- if (raw_uri != null)
- host = raw_uri.Host;
-
- int colon = host.LastIndexOf(':');
- if (colon >= 0)
- host = host.Substring(0, colon);
+ public string ContentType => Headers[HttpKnownHeaderNames.ContentType];
- string base_uri = String.Format("{0}://{1}:{2}",
- (IsSecureConnection) ? (IsWebSocketRequest ? "wss" : "https") : (IsWebSocketRequest ? "ws" : "http"),
- host, LocalEndPoint.Port);
+ public bool IsLocal => LocalEndPoint.Address.Equals(RemoteEndPoint.Address);
- if (!Uri.TryCreate(base_uri + path, UriKind.Absolute, out url))
- {
- context.ErrorMessage = WebUtility.HtmlEncode("Invalid url: " + base_uri + path);
- return; return;
- }
-
- CreateQueryString(url.Query);
-
- if (version >= HttpVersion.Version11)
+ public bool IsWebSocketRequest
+ {
+ get
{
- string t_encoding = Headers["Transfer-Encoding"];
- is_chunked = (t_encoding != null && String.Compare(t_encoding, "chunked", StringComparison.OrdinalIgnoreCase) == 0);
- // 'identity' is not valid!
- if (t_encoding != null && !is_chunked)
+ if (!SupportsWebSockets)
{
- context.Connection.SendError(null, 501);
- return;
+ return false;
}
- }
- if (!is_chunked && !cl_set)
- {
- if (String.Compare(method, "POST", StringComparison.OrdinalIgnoreCase) == 0 ||
- String.Compare(method, "PUT", StringComparison.OrdinalIgnoreCase) == 0)
+ bool foundConnectionUpgradeHeader = false;
+ if (string.IsNullOrEmpty(Headers[HttpKnownHeaderNames.Connection]) || string.IsNullOrEmpty(Headers[HttpKnownHeaderNames.Upgrade]))
{
- context.Connection.SendError(null, 411);
- return;
+ return false;
}
- }
-
- if (String.Compare(Headers["Expect"], "100-continue", StringComparison.OrdinalIgnoreCase) == 0)
- {
- var output = (HttpResponseStream)context.Connection.GetResponseStream(true);
-
- var _100continue = _textEncoding.GetASCIIEncoding().GetBytes("HTTP/1.1 100 Continue\r\n\r\n");
-
- output.InternalWrite(_100continue, 0, _100continue.Length);
- }
- }
-
- static bool MaybeUri(string s)
- {
- int p = s.IndexOf(':');
- if (p == -1)
- return false;
- if (p >= 10)
- return false;
-
- return IsPredefinedScheme(s.Substring(0, p));
- }
+ foreach (string connection in Headers.GetValues(HttpKnownHeaderNames.Connection))
+ {
+ if (string.Equals(connection, HttpKnownHeaderNames.Upgrade, StringComparison.OrdinalIgnoreCase))
+ {
+ foundConnectionUpgradeHeader = true;
+ break;
+ }
+ }
- //
- // Using a simple block of if's is twice as slow as the compiler generated
- // switch statement. But using this tuned code is faster than the
- // compiler generated code, with a million loops on x86-64:
- //
- // With "http": .10 vs .51 (first check)
- // with "https": .16 vs .51 (second check)
- // with "foo": .22 vs .31 (never found)
- // with "mailto": .12 vs .51 (last check)
- //
- //
- static bool IsPredefinedScheme(string scheme)
- {
- if (scheme == null || scheme.Length < 3)
- return false;
+ if (!foundConnectionUpgradeHeader)
+ {
+ return false;
+ }
- char c = scheme[0];
- if (c == 'h')
- return (scheme == "http" || scheme == "https");
- if (c == 'f')
- return (scheme == "file" || scheme == "ftp");
+ foreach (string upgrade in Headers.GetValues(HttpKnownHeaderNames.Upgrade))
+ {
+ if (string.Equals(upgrade, HttpWebSocket.WebSocketUpgradeToken, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ }
- if (c == 'n')
- {
- c = scheme[1];
- if (c == 'e')
- return (scheme == "news" || scheme == "net.pipe" || scheme == "net.tcp");
- if (scheme == "nntp")
- return true;
return false;
}
- if ((c == 'g' && scheme == "gopher") || (c == 'm' && scheme == "mailto"))
- return true;
-
- return false;
}
- internal static string Unquote(String str)
- {
- int start = str.IndexOf('\"');
- int end = str.LastIndexOf('\"');
- if (start >= 0 && end >= 0)
- str = str.Substring(start + 1, end - 1);
- return str.Trim();
- }
-
- internal void AddHeader(string header)
+ public bool KeepAlive
{
- int colon = header.IndexOf(':');
- if (colon == -1 || colon == 0)
- {
- context.ErrorMessage = "Bad Request";
- context.ErrorStatus = 400;
- return;
- }
-
- string name = header.Substring(0, colon).Trim();
- string val = header.Substring(colon + 1).Trim();
- string lower = name.ToLowerInvariant();
- headers.SetInternal(name, val);
- switch (lower)
+ get
{
- case "accept-language":
- user_languages = val.Split(','); // yes, only split with a ','
- break;
- case "accept":
- accept_types = val.Split(','); // yes, only split with a ','
- break;
- case "content-length":
- try
- {
- //TODO: max. content_length?
- content_length = Int64.Parse(val.Trim());
- if (content_length < 0)
- context.ErrorMessage = "Invalid Content-Length.";
- cl_set = true;
- }
- catch
- {
- context.ErrorMessage = "Invalid Content-Length.";
- }
-
- break;
- case "content-type":
- {
- var contents = val.Split(';');
- foreach (var content in contents)
- {
- var tmp = content.Trim();
- if (tmp.StartsWith("charset"))
- {
- var charset = tmp.GetValue("=");
- if (charset != null && charset.Length > 0)
- {
- try
- {
-
- // Support upnp/dlna devices - CONTENT-TYPE: text/xml ; charset="utf-8"\r\n
- charset = charset.Trim('"');
- var index = charset.IndexOf('"');
- if (index != -1) charset = charset.Substring(0, index);
-
- content_encoding = Encoding.GetEncoding(charset);
- }
- catch
- {
- context.ErrorMessage = "Invalid Content-Type header: " + charset;
- }
- }
-
- break;
- }
- }
- }
- break;
- case "referer":
- try
- {
- referrer = new Uri(val);
- }
- catch
+ if (!_keepAlive.HasValue)
+ {
+ string header = Headers[HttpKnownHeaderNames.ProxyConnection];
+ if (string.IsNullOrEmpty(header))
{
- referrer = new Uri("http://someone.is.screwing.with.the.headers.com/");
+ header = Headers[HttpKnownHeaderNames.Connection];
}
- break;
- case "cookie":
- if (cookies == null)
- cookies = new CookieCollection();
-
- string[] cookieStrings = val.Split(new char[] { ',', ';' });
- Cookie current = null;
- int version = 0;
- foreach (string cookieString in cookieStrings)
+ if (string.IsNullOrEmpty(header))
{
- string str = cookieString.Trim();
- if (str.Length == 0)
- continue;
- if (str.StartsWith("$Version"))
- {
- version = Int32.Parse(Unquote(str.Substring(str.IndexOf('=') + 1)));
- }
- else if (str.StartsWith("$Path"))
- {
- if (current != null)
- current.Path = str.Substring(str.IndexOf('=') + 1).Trim();
- }
- else if (str.StartsWith("$Domain"))
- {
- if (current != null)
- current.Domain = str.Substring(str.IndexOf('=') + 1).Trim();
- }
- else if (str.StartsWith("$Port"))
+ if (ProtocolVersion >= HttpVersion.Version11)
{
- if (current != null)
- current.Port = str.Substring(str.IndexOf('=') + 1).Trim();
+ _keepAlive = true;
}
else
{
- if (current != null)
- {
- cookies.Add(current);
- }
- current = new Cookie();
- int idx = str.IndexOf('=');
- if (idx > 0)
- {
- current.Name = str.Substring(0, idx).Trim();
- current.Value = str.Substring(idx + 1).Trim();
- }
- else
- {
- current.Name = str.Trim();
- current.Value = String.Empty;
- }
- current.Version = version;
+ header = Headers[HttpKnownHeaderNames.KeepAlive];
+ _keepAlive = !string.IsNullOrEmpty(header);
}
}
- if (current != null)
- {
- cookies.Add(current);
- }
- break;
- }
- }
-
- // returns true is the stream could be reused.
- internal bool FlushInput()
- {
- if (!HasEntityBody)
- return true;
-
- int length = 2048;
- if (content_length > 0)
- length = (int)Math.Min(content_length, (long)length);
-
- byte[] bytes = new byte[length];
- while (true)
- {
- // TODO: test if MS has a timeout when doing this
- try
- {
- var task = InputStream.ReadAsync(bytes, 0, length);
- var result = Task.WaitAll(new [] { task }, 1000);
- if (!result)
- {
- return false;
- }
- if (task.Result <= 0)
+ else
{
- return true;
+ header = header.ToLower(CultureInfo.InvariantCulture);
+ _keepAlive =
+ header.IndexOf("close", StringComparison.OrdinalIgnoreCase) < 0 ||
+ header.IndexOf("keep-alive", StringComparison.OrdinalIgnoreCase) >= 0;
}
}
- catch (ObjectDisposedException e)
- {
- input_stream = null;
- return true;
- }
- catch
- {
- return false;
- }
- }
- }
- public string[] AcceptTypes
- {
- get { return accept_types; }
+ return _keepAlive.Value;
+ }
}
- public int ClientCertificateError
+ public QueryParamCollection QueryString
{
get
{
- HttpConnection cnc = context.Connection;
- //if (cnc.ClientCertificate == null)
- // throw new InvalidOperationException("No client certificate");
- //int[] errors = cnc.ClientCertificateErrors;
- //if (errors != null && errors.Length > 0)
- // return errors[0];
- return 0;
+ QueryParamCollection queryString = new QueryParamCollection();
+ Helpers.FillFromString(queryString, Url.Query, true, ContentEncoding);
+ return queryString;
}
}
- public Encoding ContentEncoding
+ 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
{
- if (content_encoding == null)
- content_encoding = _textEncoding.GetDefaultEncoding();
- return content_encoding;
+ 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 long ContentLength64
- {
- get { return is_chunked ? -1 : content_length; }
- }
+ public Uri Url => RequestUri;
- public string ContentType
- {
- get { return headers["content-type"]; }
- }
+ public Version ProtocolVersion => _version;
- public CookieCollection Cookies
+ private static class Helpers
{
- get
+ //
+ // Get attribute off header value
+ //
+ internal static string GetCharSetValueFromHeader(string headerValue)
{
- // TODO: check if the collection is read-only
- if (cookies == null)
- cookies = new CookieCollection();
- return cookies;
- }
- }
+ const string AttrName = "charset";
- public bool HasEntityBody
- {
- get { return (content_length > 0 || is_chunked); }
- }
+ if (headerValue == null)
+ return null;
- public QueryParamCollection Headers
- {
- get { return headers; }
- }
+ int l = headerValue.Length;
+ int k = AttrName.Length;
- public string HttpMethod
- {
- get { return method; }
- }
+ // find properly separated attribute name
+ int i = 1; // start searching from 1
- public Stream InputStream
- {
- get
- {
- if (input_stream == null)
+ while (i < l)
{
- if (is_chunked || content_length > 0)
- input_stream = context.Connection.GetRequestStream(is_chunked, content_length);
- else
- input_stream = Stream.Null;
+ 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;
}
- return input_stream;
- }
- }
+ if (i < 0 || i >= l)
+ return null;
- public bool IsAuthenticated
- {
- get { return false; }
- }
+ // 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;
- public bool IsLocal
- {
- get
+ // 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)
{
- var remoteEndPoint = RemoteEndPoint;
+ 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 remoteEndPoint.Address.Equals(IPAddress.Loopback) ||
- remoteEndPoint.Address.Equals(IPAddress.IPv6Loopback) ||
- LocalEndPoint.Address.Equals(remoteEndPoint.Address);
+ // 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;
}
- }
- public bool IsSecureConnection
- {
- get { return context.Connection.IsSecure; }
- }
- public bool KeepAlive
- {
- get
+ private static string UrlDecodeStringFromStringInternal(string s, Encoding e)
{
- if (!_keepAlive.HasValue)
+ 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++)
{
- string header = Headers["Proxy-Connection"];
- if (string.IsNullOrEmpty(header))
+ char ch = s[pos];
+
+ if (ch == '+')
{
- header = Headers["Connection"];
+ ch = ' ';
}
- if (string.IsNullOrEmpty(header))
+ else if (ch == '%' && pos < count - 2)
{
- if (ProtocolVersion >= HttpVersion.Version11)
+ if (s[pos + 1] == 'u' && pos < count - 5)
{
- _keepAlive = true;
+ 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
{
- header = Headers["Keep-Alive"];
- _keepAlive = !string.IsNullOrEmpty(header);
+ 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
- {
- header = header.ToLower(CultureInfo.InvariantCulture);
- _keepAlive =
- header.IndexOf("close", StringComparison.OrdinalIgnoreCase) < 0 ||
- header.IndexOf("keep-alive", StringComparison.OrdinalIgnoreCase) >= 0;
- }
+ helper.AddChar(ch);
}
- return _keepAlive.Value;
+ return helper.GetString();
}
- }
- public IPEndPoint LocalEndPoint
- {
- get { return context.Connection.LocalEndPoint; }
- }
+ 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;
+ }
- public Version ProtocolVersion
- {
- get { return version; }
- }
+ private class UrlDecoder
+ {
+ private int _bufferSize;
- public QueryParamCollection QueryString
- {
- get { return query_string; }
- }
+ // Accumulate characters in a special array
+ private int _numChars;
+ private char[] _charBuffer;
- public string RawUrl
- {
- get { return raw_url; }
- }
+ // Accumulate bytes for decoding into characters in a special array
+ private int _numBytes;
+ private byte[] _byteBuffer;
- public IPEndPoint RemoteEndPoint
- {
- get { return context.Connection.RemoteEndPoint; }
- }
+ // Encoding to convert chars to bytes
+ private Encoding _encoding;
- public Guid RequestTraceIdentifier
- {
- get { return Guid.Empty; }
- }
+ private void FlushBytes()
+ {
+ if (_numBytes > 0)
+ {
+ _numChars += _encoding.GetChars(_byteBuffer, 0, _numBytes, _charBuffer, _numChars);
+ _numBytes = 0;
+ }
+ }
- public Uri Url
- {
- get { return url; }
- }
+ internal UrlDecoder(int bufferSize, Encoding encoding)
+ {
+ _bufferSize = bufferSize;
+ _encoding = encoding;
- public Uri UrlReferrer
- {
- get { return referrer; }
- }
+ _charBuffer = new char[bufferSize];
+ // byte buffer created on demand
+ }
- public string UserAgent
- {
- get { return headers["user-agent"]; }
- }
+ internal void AddChar(char ch)
+ {
+ if (_numBytes > 0)
+ FlushBytes();
- public string UserHostAddress
- {
- get { return LocalEndPoint.ToString(); }
- }
+ _charBuffer[_numChars++] = ch;
+ }
- public string UserHostName
- {
- get { return headers["host"]; }
- }
+ internal void AddByte(byte b)
+ {
+ {
+ if (_byteBuffer == null)
+ _byteBuffer = new byte[_bufferSize];
- public string[] UserLanguages
- {
- get { return user_languages; }
- }
+ _byteBuffer[_numBytes++] = b;
+ }
+ }
- public string ServiceName
- {
- get
- {
- return null;
+ internal string GetString()
+ {
+ if (_numBytes > 0)
+ FlushBytes();
+
+ if (_numChars > 0)
+ return new string(_charBuffer, 0, _numChars);
+ else
+ return string.Empty;
+ }
}
- }
- private bool _websocketRequestWasSet;
- private bool _websocketRequest;
- /// <summary>
- /// Gets a value indicating whether the request is a WebSocket connection request.
- /// </summary>
- /// <value>
- /// <c>true</c> if the request is a WebSocket connection request; otherwise, <c>false</c>.
- /// </value>
- public bool IsWebSocketRequest
- {
- get
+ internal static void FillFromString(QueryParamCollection nvc, string s, bool urlencoded, Encoding encoding)
{
- if (!_websocketRequestWasSet)
+ int l = (s != null) ? s.Length : 0;
+ int i = (s.Length > 0 && s[0] == '?') ? 1 : 0;
+
+ while (i < l)
{
- _websocketRequest = method == "GET" &&
- version > HttpVersion.Version10 &&
- headers.Contains("Upgrade", "websocket") &&
- headers.Contains("Connection", "Upgrade");
+ // find next & while noting first = on the way (and if there are more)
- _websocketRequestWasSet = true;
- }
+ int si = i;
+ int ti = -1;
- return _websocketRequest;
+ 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++;
+ }
}
}
}