| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559 | using System;using System.IO;using System.Net;using System.Net.Security;using System.Net.Sockets;using System.Security.Cryptography.X509Certificates;using System.Text;using System.Threading.Tasks;using MediaBrowser.Model.Cryptography;using MediaBrowser.Model.IO;using MediaBrowser.Model.Logging;using MediaBrowser.Model.Net;using MediaBrowser.Model.System;using MediaBrowser.Model.Text;using SocketHttpListener.Primitives;using System.Security.Authentication;using System.Threading;namespace SocketHttpListener.Net{    sealed class HttpConnection    {        private static AsyncCallback s_onreadCallback = new AsyncCallback(OnRead);        const int BufferSize = 8192;        Socket _socket;        Stream _stream;        HttpEndPointListener _epl;        MemoryStream _memoryStream;        byte[] _buffer;        HttpListenerContext _context;        StringBuilder _currentLine;        ListenerPrefix _prefix;        HttpRequestStream _requestStream;        HttpResponseStream _responseStream;        bool _chunked;        int _reuses;        bool _contextBound;        bool secure;        int _timeout = 90000; // 90k ms for first request, 15k ms from then on        private Timer _timer;        IPEndPoint local_ep;        HttpListener _lastListener;        X509Certificate cert;        SslStream ssl_stream;        private readonly ILogger _logger;        private readonly ICryptoProvider _cryptoProvider;        private readonly IStreamHelper _streamHelper;        private readonly ITextEncoding _textEncoding;        private readonly IFileSystem _fileSystem;        private readonly IEnvironmentInfo _environment;        public HttpConnection(ILogger logger, Socket socket, HttpEndPointListener epl, bool secure, X509Certificate cert, ICryptoProvider cryptoProvider, IStreamHelper streamHelper, ITextEncoding textEncoding, IFileSystem fileSystem, IEnvironmentInfo environment)        {            _logger = logger;            this._socket = socket;            this._epl = epl;            this.secure = secure;            this.cert = cert;            _cryptoProvider = cryptoProvider;            _streamHelper = streamHelper;            _textEncoding = textEncoding;            _fileSystem = fileSystem;            _environment = environment;            if (secure == false)            {                _stream = new SocketStream(_socket, false);            }            else            {                ssl_stream = new SslStream(new SocketStream(_socket, false), false, (t, c, ch, e) =>                {                    if (c == null)                    {                        return true;                    }                    //var c2 = c as X509Certificate2;                    //if (c2 == null)                    //{                    //    c2 = new X509Certificate2(c.GetRawCertData());                    //}                    //_clientCert = c2;                    //_clientCertErrors = new int[] { (int)e };                    return true;                });                _stream = ssl_stream;            }        }        public Stream Stream        {            get            {                return _stream;            }        }        public async Task Init()        {            _timer = new Timer(OnTimeout, null, Timeout.Infinite, Timeout.Infinite);            if (ssl_stream != null)            {                var enableAsync = true;                if (enableAsync)                {                    await ssl_stream.AuthenticateAsServerAsync(cert, false, (SslProtocols)ServicePointManager.SecurityProtocol, false).ConfigureAwait(false);                }                else                {                    ssl_stream.AuthenticateAsServer(cert, false, (SslProtocols)ServicePointManager.SecurityProtocol, false);                }            }            InitInternal();        }        private void InitInternal()        {            _contextBound = false;            _requestStream = null;            _responseStream = null;            _prefix = null;            _chunked = false;            _memoryStream = new MemoryStream();            _position = 0;            _inputState = InputState.RequestLine;            _lineState = LineState.None;            _context = new HttpListenerContext(this, _textEncoding);        }        public bool IsClosed        {            get { return (_socket == null); }        }        public int Reuses        {            get { return _reuses; }        }        public IPEndPoint LocalEndPoint        {            get            {                if (local_ep != null)                    return local_ep;                local_ep = (IPEndPoint)_socket.LocalEndPoint;                return local_ep;            }        }        public IPEndPoint RemoteEndPoint        {            get { return _socket.RemoteEndPoint as IPEndPoint; }        }        public bool IsSecure        {            get { return secure; }        }        public ListenerPrefix Prefix        {            get { return _prefix; }            set { _prefix = value; }        }        private void OnTimeout(object unused)        {            //_logger.Info("HttpConnection timer fired");            CloseSocket();            Unbind();        }        public void BeginReadRequest()        {            if (_buffer == null)                _buffer = new byte[BufferSize];            try            {                if (_reuses == 1)                    _timeout = 15000;                //_timer.Change(_timeout, Timeout.Infinite);                _stream.BeginRead(_buffer, 0, BufferSize, s_onreadCallback, this);            }            catch            {                //_timer.Change(Timeout.Infinite, Timeout.Infinite);                CloseSocket();                Unbind();            }        }        public HttpRequestStream GetRequestStream(bool chunked, long contentlength)        {            if (_requestStream == null)            {                byte[] buffer = _memoryStream.GetBuffer();                int length = (int)_memoryStream.Length;                _memoryStream = null;                if (chunked)                {                    _chunked = true;                    //_context.Response.SendChunked = true;                    _requestStream = new ChunkedInputStream(_context, _stream, buffer, _position, length - _position);                }                else                {                    _requestStream = new HttpRequestStream(_stream, buffer, _position, length - _position, contentlength);                }            }            return _requestStream;        }        public HttpResponseStream GetResponseStream(bool isExpect100Continue = false)        {            // TODO: can we get this _stream before reading the input?            if (_responseStream == null)            {                var supportsDirectSocketAccess = !_context.Response.SendChunked && !isExpect100Continue && !secure;                _responseStream = new HttpResponseStream(_stream, _context.Response, false, _streamHelper, _socket, supportsDirectSocketAccess, _environment, _fileSystem, _logger);            }            return _responseStream;        }        private static void OnRead(IAsyncResult ares)        {            HttpConnection cnc = (HttpConnection)ares.AsyncState;            cnc.OnReadInternal(ares);        }        private void OnReadInternal(IAsyncResult ares)        {            //_timer.Change(Timeout.Infinite, Timeout.Infinite);            int nread = -1;            try            {                nread = _stream.EndRead(ares);                _memoryStream.Write(_buffer, 0, nread);                if (_memoryStream.Length > 32768)                {                    SendError("Bad Request", 400);                    Close(true);                    return;                }            }            catch            {                if (_memoryStream != null && _memoryStream.Length > 0)                    SendError();                if (_socket != null)                {                    CloseSocket();                    Unbind();                }                return;            }            if (nread == 0)            {                CloseSocket();                Unbind();                return;            }            if (ProcessInput(_memoryStream))            {                if (!_context.HaveError)                    _context.Request.FinishInitialization();                if (_context.HaveError)                {                    SendError();                    Close(true);                    return;                }                if (!_epl.BindContext(_context))                {                    const int NotFoundErrorCode = 404;                    SendError(HttpStatusDescription.Get(NotFoundErrorCode), NotFoundErrorCode);                    Close(true);                    return;                }                HttpListener listener = _epl.Listener;                if (_lastListener != listener)                {                    RemoveConnection();                    listener.AddConnection(this);                    _lastListener = listener;                }                _contextBound = true;                listener.RegisterContext(_context);                return;            }            _stream.BeginRead(_buffer, 0, BufferSize, s_onreadCallback, this);        }        private void RemoveConnection()        {            if (_lastListener == null)                _epl.RemoveConnection(this);            else                _lastListener.RemoveConnection(this);        }        private enum InputState        {            RequestLine,            Headers        }        private enum LineState        {            None,            CR,            LF        }        InputState _inputState = InputState.RequestLine;        LineState _lineState = LineState.None;        int _position;        // true -> done processing        // false -> need more input        private bool ProcessInput(MemoryStream ms)        {            byte[] buffer = ms.GetBuffer();            int len = (int)ms.Length;            int used = 0;            string line;            while (true)            {                if (_context.HaveError)                    return true;                if (_position >= len)                    break;                try                {                    line = ReadLine(buffer, _position, len - _position, ref used);                    _position += used;                }                catch                {                    _context.ErrorMessage = "Bad request";                    _context.ErrorStatus = 400;                    return true;                }                if (line == null)                    break;                if (line == "")                {                    if (_inputState == InputState.RequestLine)                        continue;                    _currentLine = null;                    ms = null;                    return true;                }                if (_inputState == InputState.RequestLine)                {                    _context.Request.SetRequestLine(line);                    _inputState = InputState.Headers;                }                else                {                    try                    {                        _context.Request.AddHeader(line);                    }                    catch (Exception e)                    {                        _context.ErrorMessage = e.Message;                        _context.ErrorStatus = 400;                        return true;                    }                }            }            if (used == len)            {                ms.SetLength(0);                _position = 0;            }            return false;        }        private string ReadLine(byte[] buffer, int offset, int len, ref int used)        {            if (_currentLine == null)                _currentLine = new StringBuilder(128);            int last = offset + len;            used = 0;            for (int i = offset; i < last && _lineState != LineState.LF; i++)            {                used++;                byte b = buffer[i];                if (b == 13)                {                    _lineState = LineState.CR;                }                else if (b == 10)                {                    _lineState = LineState.LF;                }                else                {                    _currentLine.Append((char)b);                }            }            string result = null;            if (_lineState == LineState.LF)            {                _lineState = LineState.None;                result = _currentLine.ToString();                _currentLine.Length = 0;            }            return result;        }        public void SendError(string msg, int status)        {            try            {                HttpListenerResponse response = _context.Response;                response.StatusCode = status;                response.ContentType = "text/html";                string description = HttpStatusDescription.Get(status);                string str;                if (msg != null)                    str = string.Format("<h1>{0} ({1})</h1>", description, msg);                else                    str = string.Format("<h1>{0}</h1>", description);                byte[] error = _textEncoding.GetDefaultEncoding().GetBytes(str);                response.Close(error, false);            }            catch            {                // response was already closed            }        }        public void SendError()        {            SendError(_context.ErrorMessage, _context.ErrorStatus);        }        private void Unbind()        {            if (_contextBound)            {                _epl.UnbindContext(_context);                _contextBound = false;            }        }        public void Close()        {            Close(false);        }        private void CloseSocket()        {            if (_socket == null)                return;            try            {                _socket.Close();            }            catch { }            finally            {                _socket = null;            }            RemoveConnection();        }        internal void Close(bool force)        {            if (_socket != null)            {                Stream st = GetResponseStream();                if (st != null)                    st.Close();                _responseStream = null;            }            if (_socket != null)            {                force |= !_context.Request.KeepAlive;                if (!force)                    force = (string.Equals(_context.Response.Headers["connection"], "close", StringComparison.OrdinalIgnoreCase));                if (!force && _context.Request.FlushInput())                {                    if (_chunked && _context.Response.ForceCloseChunked == false)                    {                        // Don't close. Keep working.                        _reuses++;                        Unbind();                        InitInternal();                        BeginReadRequest();                        return;                    }                    _reuses++;                    Unbind();                    InitInternal();                    BeginReadRequest();                    return;                }                Socket s = _socket;                _socket = null;                try                {                    if (s != null)                        s.Shutdown(SocketShutdown.Both);                }                catch                {                }                finally                {                    if (s != null)                    {                        try                        {                            s.Close();                        }                        catch { }                    }                }                Unbind();                RemoveConnection();                return;            }        }    }}
 |