| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660 | using System;using System.Collections.Specialized;using System.Globalization;using System.IO;using System.Net;using System.Text;using System.Threading.Tasks;using MediaBrowser.Model.Net;using MediaBrowser.Model.Services;using MediaBrowser.Model.Text;using SocketHttpListener.Primitives;namespace SocketHttpListener.Net{    public sealed 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;        }        static char[] separators = new char[] { ' ' };        internal void SetRequestLine(string req)        {            string[] parts = req.Split(separators, 3);            if (parts.Length != 3)            {                context.ErrorMessage = "Invalid request line (parts).";                return;            }            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;            }        }        void CreateQueryString(string query)        {            if (query == null || query.Length == 0)            {                query_string = new QueryParamCollection();                return;            }            query_string = new QueryParamCollection();            if (query[0] == '?')                query = query.Substring(1);            string[] components = query.Split('&');            foreach (string kv in components)            {                int pos = kv.IndexOf('=');                if (pos == -1)                {                    query_string.Add(null, WebUtility.UrlDecode(kv));                }                else                {                    string key = WebUtility.UrlDecode(kv.Substring(0, pos));                    string val = WebUtility.UrlDecode(kv.Substring(pos + 1));                    query_string.Add(key, val);                }            }        }        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);            string base_uri = String.Format("{0}://{1}:{2}",                (IsSecureConnection) ? (IsWebSocketRequest ? "wss" : "https") : (IsWebSocketRequest ? "ws" : "http"),                                host, LocalEndPoint.Port);            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)            {                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)                {                    context.Connection.SendError(null, 501);                    return;                }            }            if (!is_chunked && !cl_set)            {                if (String.Compare(method, "POST", StringComparison.OrdinalIgnoreCase) == 0 ||                    String.Compare(method, "PUT", StringComparison.OrdinalIgnoreCase) == 0)                {                    context.Connection.SendError(null, 411);                    return;                }            }            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));        }        //        // 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;            char c = scheme[0];            if (c == 'h')                return (scheme == "http" || scheme == "https");            if (c == 'f')                return (scheme == "file" || scheme == "ftp");            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)        {            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)            {                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                    {                        referrer = new Uri("http://someone.is.screwing.with.the.headers.com/");                    }                    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)                    {                        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 (current != null)                                current.Port = str.Substring(str.IndexOf('=') + 1).Trim();                        }                        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;                        }                    }                    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)                    {                        return true;                    }                }                catch (ObjectDisposedException e)                {                    input_stream = null;                    return true;                }                catch                {                    return false;                }            }        }        public string[] AcceptTypes        {            get { return accept_types; }        }        public int ClientCertificateError        {            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;            }        }        public Encoding ContentEncoding        {            get            {                if (content_encoding == null)                    content_encoding = _textEncoding.GetDefaultEncoding();                return content_encoding;            }        }        public long ContentLength64        {            get { return is_chunked ? -1 : content_length; }        }        public string ContentType        {            get { return headers["content-type"]; }        }        public CookieCollection Cookies        {            get            {                // TODO: check if the collection is read-only                if (cookies == null)                    cookies = new CookieCollection();                return cookies;            }        }        public bool HasEntityBody        {            get { return (content_length > 0 || is_chunked); }        }        public QueryParamCollection Headers        {            get { return headers; }        }        public string HttpMethod        {            get { return method; }        }        public Stream InputStream        {            get            {                if (input_stream == null)                {                    if (is_chunked || content_length > 0)                        input_stream = context.Connection.GetRequestStream(is_chunked, content_length);                    else                        input_stream = Stream.Null;                }                return input_stream;            }        }        public bool IsAuthenticated        {            get { return false; }        }        public bool IsLocal        {            get { return RemoteEndPoint.IpAddress.Equals(IpAddressInfo.Loopback) || RemoteEndPoint.IpAddress.Equals(IpAddressInfo.IPv6Loopback) || LocalEndPoint.IpAddress.Equals(RemoteEndPoint.IpAddress); }        }        public bool IsSecureConnection        {            get { return context.Connection.IsSecure; }        }        public bool KeepAlive        {            get            {                if (!_keepAlive.HasValue)                {                    string header = Headers["Proxy-Connection"];                    if (string.IsNullOrEmpty(header))                    {                        header = Headers["Connection"];                    }                    if (string.IsNullOrEmpty(header))                    {                        if (ProtocolVersion >= HttpVersion.Version11)                        {                            _keepAlive = true;                        }                        else                        {                            header = Headers["Keep-Alive"];                            _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 IpEndPointInfo LocalEndPoint        {            get { return context.Connection.LocalEndPoint; }        }        public Version ProtocolVersion        {            get { return version; }        }        public QueryParamCollection QueryString        {            get { return query_string; }        }        public string RawUrl        {            get { return raw_url; }        }        public IpEndPointInfo RemoteEndPoint        {            get { return context.Connection.RemoteEndPoint; }        }        public Guid RequestTraceIdentifier        {            get { return Guid.Empty; }        }        public Uri Url        {            get { return url; }        }        public Uri UrlReferrer        {            get { return referrer; }        }        public string UserAgent        {            get { return headers["user-agent"]; }        }        public string UserHostAddress        {            get { return LocalEndPoint.ToString(); }        }        public string UserHostName        {            get { return headers["host"]; }        }        public string[] UserLanguages        {            get { return user_languages; }        }        public string ServiceName        {            get            {                return null;            }        }        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            {                if (!_websocketRequestWasSet)                {                    _websocketRequest = method == "GET" &&                                        version > HttpVersion.Version10 &&                                        headers.Contains("Upgrade", "websocket") &&                                        headers.Contains("Connection", "Upgrade");                    _websocketRequestWasSet = true;                }                return _websocketRequest;            }        }        public Task<ICertificate> GetClientCertificateAsync()        {            return Task.FromResult<ICertificate>(null);        }    }}
 |