Explorar o código

Reduce the amount of exceptions thrown

Bond_009 %!s(int64=6) %!d(string=hai) anos
pai
achega
37ea50a572

+ 6 - 3
Emby.Dlna/PlayTo/Device.cs

@@ -1126,6 +1126,11 @@ namespace Emby.Dlna.PlayTo
 
         private void OnPlaybackStart(uBaseObject mediaInfo)
         {
+            if (string.IsNullOrWhiteSpace(mediaInfo.Url))
+            {
+                return;
+            }
+
             PlaybackStart?.Invoke(this, new PlaybackStartEventArgs
             {
                 MediaInfo = mediaInfo
@@ -1134,8 +1139,7 @@ namespace Emby.Dlna.PlayTo
 
         private void OnPlaybackProgress(uBaseObject mediaInfo)
         {
-            var mediaUrl = mediaInfo.Url;
-            if (string.IsNullOrWhiteSpace(mediaUrl))
+            if (string.IsNullOrWhiteSpace(mediaInfo.Url))
             {
                 return;
             }
@@ -1148,7 +1152,6 @@ namespace Emby.Dlna.PlayTo
 
         private void OnPlaybackStop(uBaseObject mediaInfo)
         {
-
             PlaybackStopped?.Invoke(this, new PlaybackStoppedEventArgs
             {
                 MediaInfo = mediaInfo

+ 1 - 1
Emby.Server.Implementations/Diagnostics/CommonProcess.cs

@@ -130,7 +130,7 @@ namespace Emby.Server.Implementations.Diagnostics
 
         public void Dispose()
         {
-            _process.Dispose();
+            _process?.Dispose();
         }
     }
 }

+ 9 - 18
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -278,6 +278,7 @@ namespace Emby.Server.Implementations.Library
             {
                 throw new ArgumentNullException(nameof(item));
             }
+
             if (item is IItemByName)
             {
                 if (!(item is MusicArtist))
@@ -285,18 +286,7 @@ namespace Emby.Server.Implementations.Library
                     return;
                 }
             }
-
-            else if (item.IsFolder)
-            {
-                //if (!(item is ICollectionFolder) && !(item is UserView) && !(item is Channel) && !(item is AggregateFolder))
-                //{
-                //    if (item.SourceType != SourceType.Library)
-                //    {
-                //        return;
-                //    }
-                //}
-            }
-            else
+            else if (!item.IsFolder)
             {
                 if (!(item is Video) && !(item is LiveTvChannel))
                 {
@@ -371,19 +361,20 @@ namespace Emby.Server.Implementations.Library
 
             foreach (var metadataPath in GetMetadataPaths(item, children))
             {
-                _logger.LogDebug("Deleting path {0}", metadataPath);
+                if (!Directory.Exists(metadataPath))
+                {
+                    continue;
+                }
+
+                _logger.LogDebug("Deleting path {MetadataPath}", metadataPath);
 
                 try
                 {
                     Directory.Delete(metadataPath, true);
-                }
-                catch (IOException)
-                {
-
                 }
                 catch (Exception ex)
                 {
-                    _logger.LogError(ex, "Error deleting {metadataPath}", metadataPath);
+                    _logger.LogError(ex, "Error deleting {MetadataPath}", metadataPath);
                 }
             }
 

+ 2 - 2
Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -105,8 +105,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             _mediaSourceManager = mediaSourceManager;
             _streamHelper = streamHelper;
 
-            _seriesTimerProvider = new SeriesTimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers"));
-            _timerProvider = new TimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "timers"), _logger);
+            _seriesTimerProvider = new SeriesTimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers.json"));
+            _timerProvider = new TimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "timers.json"), _logger);
             _timerProvider.TimerFired += _timerProvider_TimerFired;
 
             _config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;

+ 10 - 16
Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs

@@ -2,7 +2,6 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
-using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Serialization;
 using Microsoft.Extensions.Logging;
 
@@ -32,32 +31,28 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             {
                 if (_items == null)
                 {
+                    if (!File.Exists(_dataPath))
+                    {
+                        return new List<T>();
+                    }
+
                     Logger.LogInformation("Loading live tv data from {0}", _dataPath);
                     _items = GetItemsFromFile(_dataPath);
                 }
+
                 return _items.ToList();
             }
         }
 
         private List<T> GetItemsFromFile(string path)
         {
-            var jsonFile = path + ".json";
-
-            if (!File.Exists(jsonFile))
-            {
-                return new List<T>();
-            }
-
             try
             {
-                return _jsonSerializer.DeserializeFromFile<List<T>>(jsonFile) ?? new List<T>();
-            }
-            catch (IOException)
-            {
+                return _jsonSerializer.DeserializeFromFile<List<T>>(path);
             }
             catch (Exception ex)
             {
-                Logger.LogError(ex, "Error deserializing {jsonFile}", jsonFile);
+                Logger.LogError(ex, "Error deserializing {Path}", path);
             }
 
             return new List<T>();
@@ -70,12 +65,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                 throw new ArgumentNullException(nameof(newList));
             }
 
-            var file = _dataPath + ".json";
-            Directory.CreateDirectory(Path.GetDirectoryName(file));
+            Directory.CreateDirectory(Path.GetDirectoryName(_dataPath));
 
             lock (_fileDataLock)
             {
-                _jsonSerializer.SerializeToFile(newList, file);
+                _jsonSerializer.SerializeToFile(newList, _dataPath);
                 _items = newList;
             }
         }

+ 4 - 0
Emby.Server.Implementations/MediaEncoder/EncodingManager.cs

@@ -202,6 +202,10 @@ namespace Emby.Server.Implementations.MediaEncoder
         private static List<string> GetSavedChapterImages(Video video, IDirectoryService directoryService)
         {
             var path = GetChapterImagesPath(video);
+            if (!Directory.Exists(path))
+            {
+                return new List<string>();
+            }
 
             try
             {

+ 5 - 0
MediaBrowser.Api/ApiEntryPoint.cs

@@ -172,6 +172,11 @@ namespace MediaBrowser.Api
         {
             var path = _config.ApplicationPaths.GetTranscodingTempPath();
 
+            if (!Directory.Exists(path))
+            {
+                return;
+            }
+
             foreach (var file in _fileSystem.GetFilePaths(path, true))
             {
                 _fileSystem.DeleteFile(file);

+ 1 - 1
MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs

@@ -74,7 +74,7 @@ namespace MediaBrowser.Controller.MediaEncoding
         /// <param name="inputFiles">The input files.</param>
         /// <param name="protocol">The protocol.</param>
         /// <returns>System.String.</returns>
-        string GetInputArgument(string[] inputFiles, MediaProtocol protocol);
+        string GetInputArgument(IReadOnlyList<string> inputFiles, MediaProtocol protocol);
 
         /// <summary>
         /// Gets the time parameter.

+ 14 - 2
MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs

@@ -5,6 +5,7 @@ using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.IO;
+using Microsoft.Extensions.Logging;
 
 namespace MediaBrowser.LocalMetadata.Images
 {
@@ -12,11 +13,16 @@ namespace MediaBrowser.LocalMetadata.Images
     {
         private readonly IServerConfigurationManager _config;
         private readonly IFileSystem _fileSystem;
+        private readonly ILogger _logger;
 
-        public InternalMetadataFolderImageProvider(IServerConfigurationManager config, IFileSystem fileSystem)
+        public InternalMetadataFolderImageProvider(
+            IServerConfigurationManager config,
+            IFileSystem fileSystem,
+            ILogger<InternalMetadataFolderImageProvider> logger)
         {
             _config = config;
             _fileSystem = fileSystem;
+            _logger = logger;
         }
 
         public string Name => "Internal Images";
@@ -53,12 +59,18 @@ namespace MediaBrowser.LocalMetadata.Images
         {
             var path = item.GetInternalMetadataPath();
 
+            if (!Directory.Exists(path))
+            {
+                return new List<LocalImageInfo>();
+            }
+
             try
             {
                 return new LocalImageProvider(_fileSystem).GetImages(item, path, false, directoryService);
             }
-            catch (IOException)
+            catch (IOException ex)
             {
+                _logger.LogError(ex, "Error while getting images for {Library}", item.Name);
                 return new List<LocalImageInfo>();
             }
         }

+ 3 - 3
MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs

@@ -6,11 +6,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
 {
     public static class EncodingUtils
     {
-        public static string GetInputArgument(List<string> inputFiles, MediaProtocol protocol)
+        public static string GetInputArgument(IReadOnlyList<string> inputFiles, MediaProtocol protocol)
         {
             if (protocol != MediaProtocol.File)
             {
-                var url = inputFiles.First();
+                var url = inputFiles[0];
 
                 return string.Format("\"{0}\"", url);
             }
@@ -29,7 +29,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
             // If there's more than one we'll need to use the concat command
             if (inputFiles.Count > 1)
             {
-                var files = string.Join("|", inputFiles.Select(NormalizePath).ToArray());
+                var files = string.Join("|", inputFiles.Select(NormalizePath));
 
                 return string.Format("concat:\"{0}\"", files);
             }

+ 33 - 31
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -334,10 +334,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
         /// <param name="protocol">The protocol.</param>
         /// <returns>System.String.</returns>
         /// <exception cref="ArgumentException">Unrecognized InputType</exception>
-        public string GetInputArgument(string[] inputFiles, MediaProtocol protocol)
-        {
-            return EncodingUtils.GetInputArgument(inputFiles.ToList(), protocol);
-        }
+        public string GetInputArgument(IReadOnlyList<string> inputFiles, MediaProtocol protocol)
+            => EncodingUtils.GetInputArgument(inputFiles, protocol);
 
         /// <summary>
         /// Gets the media info internal.
@@ -354,8 +352,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
             CancellationToken cancellationToken)
         {
             var args = extractChapters
-                ? "{0} -i {1} -threads 0 -v info -print_format json -show_streams -show_chapters -show_format"
-                : "{0} -i {1} -threads 0 -v info -print_format json -show_streams -show_format";
+                ? "{0} -i {1} -threads 0 -v warning -print_format json -show_streams -show_chapters -show_format"
+                : "{0} -i {1} -threads 0 -v warning -print_format json -show_streams -show_format";
+            args = string.Format(args, probeSizeArgument, inputPath).Trim();
 
             var process = _processFactory.Create(new ProcessOptions
             {
@@ -364,8 +363,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
                 // Must consume both or ffmpeg may hang due to deadlocks. See comments below.
                 RedirectStandardOutput = true,
+
                 FileName = FFprobePath,
-                Arguments = string.Format(args, probeSizeArgument, inputPath).Trim(),
+                Arguments = args,
+
 
                 IsHidden = true,
                 ErrorDialog = false,
@@ -383,43 +384,44 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
             using (var processWrapper = new ProcessWrapper(process, this, _logger))
             {
+                _logger.LogDebug("Starting ffprobe with args {Args}", args);
                 StartProcess(processWrapper);
 
+                InternalMediaInfoResult result;
                 try
                 {
-                    //process.BeginErrorReadLine();
+                    result = await _jsonSerializer.DeserializeFromStreamAsync<InternalMediaInfoResult>(process.StandardOutput.BaseStream).ConfigureAwait(false);
+                }
+                catch
+                {
+                    StopProcess(processWrapper, 100);
 
-                    var result = await _jsonSerializer.DeserializeFromStreamAsync<InternalMediaInfoResult>(process.StandardOutput.BaseStream).ConfigureAwait(false);
+                    throw;
+                }
 
-                    if (result == null || (result.streams == null && result.format == null))
-                    {
-                        throw new Exception("ffprobe failed - streams and format are both null.");
-                    }
+                if (result == null || (result.streams == null && result.format == null))
+                {
+                    throw new Exception("ffprobe failed - streams and format are both null.");
+                }
 
-                    if (result.streams != null)
+                if (result.streams != null)
+                {
+                    // Normalize aspect ratio if invalid
+                    foreach (var stream in result.streams)
                     {
-                        // Normalize aspect ratio if invalid
-                        foreach (var stream in result.streams)
+                        if (string.Equals(stream.display_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase))
                         {
-                            if (string.Equals(stream.display_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase))
-                            {
-                                stream.display_aspect_ratio = string.Empty;
-                            }
-                            if (string.Equals(stream.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase))
-                            {
-                                stream.sample_aspect_ratio = string.Empty;
-                            }
+                            stream.display_aspect_ratio = string.Empty;
                         }
-                    }
 
-                    return new ProbeResultNormalizer(_logger, FileSystem).GetMediaInfo(result, videoType, isAudio, primaryPath, protocol);
+                        if (string.Equals(stream.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase))
+                        {
+                            stream.sample_aspect_ratio = string.Empty;
+                        }
+                    }
                 }
-                catch
-                {
-                    StopProcess(processWrapper, 100);
 
-                    throw;
-                }
+                return new ProbeResultNormalizer(_logger, FileSystem).GetMediaInfo(result, videoType, isAudio, primaryPath, protocol);
             }
         }
 

+ 10 - 3
MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs

@@ -16,7 +16,7 @@ namespace MediaBrowser.Providers.MediaInfo
         private readonly ILocalizationManager _localization;
         private readonly IFileSystem _fileSystem;
 
-        private string[] SubtitleExtensions = new[]
+        private static readonly HashSet<string> SubtitleExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
         {
             ".srt",
             ".ssa",
@@ -49,9 +49,16 @@ namespace MediaBrowser.Providers.MediaInfo
 
             startIndex += streams.Count;
 
+            string folder = video.GetInternalMetadataPath();
+
+            if (!Directory.Exists(folder))
+            {
+                return streams;
+            }
+
             try
             {
-                AddExternalSubtitleStreams(streams, video.GetInternalMetadataPath(), video.Path, startIndex, directoryService, clearCache);
+                AddExternalSubtitleStreams(streams, folder, video.Path, startIndex, directoryService, clearCache);
             }
             catch (IOException)
             {
@@ -105,7 +112,7 @@ namespace MediaBrowser.Providers.MediaInfo
             {
                 var extension = Path.GetExtension(fullName);
 
-                if (!SubtitleExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
+                if (!SubtitleExtensions.Contains(extension))
                 {
                     continue;
                 }

+ 532 - 0
SocketHttpListener/Net/HttpConnection.cs

@@ -0,0 +1,532 @@
+using System;
+using System.IO;
+using System.Net;
+using System.Net.Security;
+using System.Net.Sockets;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Cryptography;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.System;
+using Microsoft.Extensions.Logging;
+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;
+        IPEndPoint local_ep;
+        HttpListener _lastListener;
+        X509Certificate cert;
+        SslStream ssl_stream;
+
+        private readonly ILogger _logger;
+        private readonly ICryptoProvider _cryptoProvider;
+        private readonly IStreamHelper _streamHelper;
+        private readonly IFileSystem _fileSystem;
+        private readonly IEnvironmentInfo _environment;
+
+        public HttpConnection(ILogger logger, Socket socket, HttpEndPointListener epl, bool secure,
+            X509Certificate cert, ICryptoProvider cryptoProvider, IStreamHelper streamHelper, IFileSystem fileSystem,
+            IEnvironmentInfo environment)
+        {
+            _logger = logger;
+            this._socket = socket;
+            this._epl = epl;
+            this.secure = secure;
+            this.cert = cert;
+            _cryptoProvider = cryptoProvider;
+            _streamHelper = streamHelper;
+            _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 => _stream;
+
+        public async Task Init()
+        {
+            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);
+        }
+
+        public bool IsClosed => (_socket == null);
+
+        public int Reuses => _reuses;
+
+        public IPEndPoint LocalEndPoint
+        {
+            get
+            {
+                if (local_ep != null)
+                    return local_ep;
+
+                local_ep = (IPEndPoint)_socket.LocalEndPoint;
+                return local_ep;
+            }
+        }
+
+        public IPEndPoint RemoteEndPoint => _socket.RemoteEndPoint as IPEndPoint;
+
+        public bool IsSecure => secure;
+
+        public ListenerPrefix Prefix
+        {
+            get => _prefix;
+            set => _prefix = value;
+        }
+
+        private void OnTimeout(object unused)
+        {
+            //_logger.LogInformation("HttpConnection timer fired");
+            CloseSocket();
+            Unbind();
+        }
+
+        public void BeginReadRequest()
+        {
+            if (_buffer == null)
+            {
+                _buffer = new byte[BufferSize];
+            }
+
+            try
+            {
+                _stream.BeginRead(_buffer, 0, BufferSize, s_onreadCallback, this);
+            }
+            catch
+            {
+                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)
+        {
+            var cnc = (HttpConnection)ares.AsyncState;
+            cnc.OnReadInternal(ares);
+        }
+
+        private void OnReadInternal(IAsyncResult ares)
+        {
+            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 = Encoding.UTF8.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
+                {
+                    s?.Shutdown(SocketShutdown.Both);
+                }
+                catch
+                {
+                }
+                finally
+                {
+                    try
+                    {
+                        s?.Close();
+                    }
+                    catch { }
+                }
+                Unbind();
+                RemoveConnection();
+                return;
+            }
+        }
+    }
+}