Reduce the amount of exceptions thrown
This commit is contained in:
parent
10a0d6bdba
commit
37ea50a572
|
@ -1126,6 +1126,11 @@ namespace Emby.Dlna.PlayTo
|
||||||
|
|
||||||
private void OnPlaybackStart(uBaseObject mediaInfo)
|
private void OnPlaybackStart(uBaseObject mediaInfo)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(mediaInfo.Url))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
PlaybackStart?.Invoke(this, new PlaybackStartEventArgs
|
PlaybackStart?.Invoke(this, new PlaybackStartEventArgs
|
||||||
{
|
{
|
||||||
MediaInfo = mediaInfo
|
MediaInfo = mediaInfo
|
||||||
|
@ -1134,8 +1139,7 @@ namespace Emby.Dlna.PlayTo
|
||||||
|
|
||||||
private void OnPlaybackProgress(uBaseObject mediaInfo)
|
private void OnPlaybackProgress(uBaseObject mediaInfo)
|
||||||
{
|
{
|
||||||
var mediaUrl = mediaInfo.Url;
|
if (string.IsNullOrWhiteSpace(mediaInfo.Url))
|
||||||
if (string.IsNullOrWhiteSpace(mediaUrl))
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1148,7 +1152,6 @@ namespace Emby.Dlna.PlayTo
|
||||||
|
|
||||||
private void OnPlaybackStop(uBaseObject mediaInfo)
|
private void OnPlaybackStop(uBaseObject mediaInfo)
|
||||||
{
|
{
|
||||||
|
|
||||||
PlaybackStopped?.Invoke(this, new PlaybackStoppedEventArgs
|
PlaybackStopped?.Invoke(this, new PlaybackStoppedEventArgs
|
||||||
{
|
{
|
||||||
MediaInfo = mediaInfo
|
MediaInfo = mediaInfo
|
||||||
|
|
|
@ -130,7 +130,7 @@ namespace Emby.Server.Implementations.Diagnostics
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_process.Dispose();
|
_process?.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -278,6 +278,7 @@ namespace Emby.Server.Implementations.Library
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(item));
|
throw new ArgumentNullException(nameof(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item is IItemByName)
|
if (item is IItemByName)
|
||||||
{
|
{
|
||||||
if (!(item is MusicArtist))
|
if (!(item is MusicArtist))
|
||||||
|
@ -285,18 +286,7 @@ namespace Emby.Server.Implementations.Library
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (!item.IsFolder)
|
||||||
else if (item.IsFolder)
|
|
||||||
{
|
|
||||||
//if (!(item is ICollectionFolder) && !(item is UserView) && !(item is Channel) && !(item is AggregateFolder))
|
|
||||||
//{
|
|
||||||
// if (item.SourceType != SourceType.Library)
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if (!(item is Video) && !(item is LiveTvChannel))
|
if (!(item is Video) && !(item is LiveTvChannel))
|
||||||
{
|
{
|
||||||
|
@ -371,19 +361,20 @@ namespace Emby.Server.Implementations.Library
|
||||||
|
|
||||||
foreach (var metadataPath in GetMetadataPaths(item, children))
|
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
|
try
|
||||||
{
|
{
|
||||||
Directory.Delete(metadataPath, true);
|
Directory.Delete(metadataPath, true);
|
||||||
}
|
|
||||||
catch (IOException)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error deleting {metadataPath}", metadataPath);
|
_logger.LogError(ex, "Error deleting {MetadataPath}", metadataPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,8 +105,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||||
_mediaSourceManager = mediaSourceManager;
|
_mediaSourceManager = mediaSourceManager;
|
||||||
_streamHelper = streamHelper;
|
_streamHelper = streamHelper;
|
||||||
|
|
||||||
_seriesTimerProvider = new SeriesTimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers"));
|
_seriesTimerProvider = new SeriesTimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers.json"));
|
||||||
_timerProvider = new TimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "timers"), _logger);
|
_timerProvider = new TimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "timers.json"), _logger);
|
||||||
_timerProvider.TimerFired += _timerProvider_TimerFired;
|
_timerProvider.TimerFired += _timerProvider_TimerFired;
|
||||||
|
|
||||||
_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
|
_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
|
||||||
|
|
|
@ -2,7 +2,6 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using MediaBrowser.Model.IO;
|
|
||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Model.Serialization;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
@ -32,32 +31,28 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||||
{
|
{
|
||||||
if (_items == null)
|
if (_items == null)
|
||||||
{
|
{
|
||||||
|
if (!File.Exists(_dataPath))
|
||||||
|
{
|
||||||
|
return new List<T>();
|
||||||
|
}
|
||||||
|
|
||||||
Logger.LogInformation("Loading live tv data from {0}", _dataPath);
|
Logger.LogInformation("Loading live tv data from {0}", _dataPath);
|
||||||
_items = GetItemsFromFile(_dataPath);
|
_items = GetItemsFromFile(_dataPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _items.ToList();
|
return _items.ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<T> GetItemsFromFile(string path)
|
private List<T> GetItemsFromFile(string path)
|
||||||
{
|
{
|
||||||
var jsonFile = path + ".json";
|
|
||||||
|
|
||||||
if (!File.Exists(jsonFile))
|
|
||||||
{
|
|
||||||
return new List<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return _jsonSerializer.DeserializeFromFile<List<T>>(jsonFile) ?? new List<T>();
|
return _jsonSerializer.DeserializeFromFile<List<T>>(path);
|
||||||
}
|
|
||||||
catch (IOException)
|
|
||||||
{
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.LogError(ex, "Error deserializing {jsonFile}", jsonFile);
|
Logger.LogError(ex, "Error deserializing {Path}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new List<T>();
|
return new List<T>();
|
||||||
|
@ -70,12 +65,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||||
throw new ArgumentNullException(nameof(newList));
|
throw new ArgumentNullException(nameof(newList));
|
||||||
}
|
}
|
||||||
|
|
||||||
var file = _dataPath + ".json";
|
Directory.CreateDirectory(Path.GetDirectoryName(_dataPath));
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(file));
|
|
||||||
|
|
||||||
lock (_fileDataLock)
|
lock (_fileDataLock)
|
||||||
{
|
{
|
||||||
_jsonSerializer.SerializeToFile(newList, file);
|
_jsonSerializer.SerializeToFile(newList, _dataPath);
|
||||||
_items = newList;
|
_items = newList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,6 +202,10 @@ namespace Emby.Server.Implementations.MediaEncoder
|
||||||
private static List<string> GetSavedChapterImages(Video video, IDirectoryService directoryService)
|
private static List<string> GetSavedChapterImages(Video video, IDirectoryService directoryService)
|
||||||
{
|
{
|
||||||
var path = GetChapterImagesPath(video);
|
var path = GetChapterImagesPath(video);
|
||||||
|
if (!Directory.Exists(path))
|
||||||
|
{
|
||||||
|
return new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
@ -172,6 +172,11 @@ namespace MediaBrowser.Api
|
||||||
{
|
{
|
||||||
var path = _config.ApplicationPaths.GetTranscodingTempPath();
|
var path = _config.ApplicationPaths.GetTranscodingTempPath();
|
||||||
|
|
||||||
|
if (!Directory.Exists(path))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var file in _fileSystem.GetFilePaths(path, true))
|
foreach (var file in _fileSystem.GetFilePaths(path, true))
|
||||||
{
|
{
|
||||||
_fileSystem.DeleteFile(file);
|
_fileSystem.DeleteFile(file);
|
||||||
|
|
|
@ -74,7 +74,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
/// <param name="inputFiles">The input files.</param>
|
/// <param name="inputFiles">The input files.</param>
|
||||||
/// <param name="protocol">The protocol.</param>
|
/// <param name="protocol">The protocol.</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
string GetInputArgument(string[] inputFiles, MediaProtocol protocol);
|
string GetInputArgument(IReadOnlyList<string> inputFiles, MediaProtocol protocol);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the time parameter.
|
/// Gets the time parameter.
|
||||||
|
|
|
@ -5,6 +5,7 @@ using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Audio;
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace MediaBrowser.LocalMetadata.Images
|
namespace MediaBrowser.LocalMetadata.Images
|
||||||
{
|
{
|
||||||
|
@ -12,11 +13,16 @@ namespace MediaBrowser.LocalMetadata.Images
|
||||||
{
|
{
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
private readonly IFileSystem _fileSystem;
|
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;
|
_config = config;
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name => "Internal Images";
|
public string Name => "Internal Images";
|
||||||
|
@ -53,12 +59,18 @@ namespace MediaBrowser.LocalMetadata.Images
|
||||||
{
|
{
|
||||||
var path = item.GetInternalMetadataPath();
|
var path = item.GetInternalMetadataPath();
|
||||||
|
|
||||||
|
if (!Directory.Exists(path))
|
||||||
|
{
|
||||||
|
return new List<LocalImageInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return new LocalImageProvider(_fileSystem).GetImages(item, path, false, directoryService);
|
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>();
|
return new List<LocalImageInfo>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
{
|
{
|
||||||
public static class EncodingUtils
|
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)
|
if (protocol != MediaProtocol.File)
|
||||||
{
|
{
|
||||||
var url = inputFiles.First();
|
var url = inputFiles[0];
|
||||||
|
|
||||||
return string.Format("\"{0}\"", url);
|
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 there's more than one we'll need to use the concat command
|
||||||
if (inputFiles.Count > 1)
|
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);
|
return string.Format("concat:\"{0}\"", files);
|
||||||
}
|
}
|
||||||
|
|
|
@ -334,10 +334,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
/// <param name="protocol">The protocol.</param>
|
/// <param name="protocol">The protocol.</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
/// <exception cref="ArgumentException">Unrecognized InputType</exception>
|
/// <exception cref="ArgumentException">Unrecognized InputType</exception>
|
||||||
public string GetInputArgument(string[] inputFiles, MediaProtocol protocol)
|
public string GetInputArgument(IReadOnlyList<string> inputFiles, MediaProtocol protocol)
|
||||||
{
|
=> EncodingUtils.GetInputArgument(inputFiles, protocol);
|
||||||
return EncodingUtils.GetInputArgument(inputFiles.ToList(), protocol);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the media info internal.
|
/// Gets the media info internal.
|
||||||
|
@ -354,8 +352,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var args = extractChapters
|
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 warning -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_format";
|
||||||
|
args = string.Format(args, probeSizeArgument, inputPath).Trim();
|
||||||
|
|
||||||
var process = _processFactory.Create(new ProcessOptions
|
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.
|
// Must consume both or ffmpeg may hang due to deadlocks. See comments below.
|
||||||
RedirectStandardOutput = true,
|
RedirectStandardOutput = true,
|
||||||
|
|
||||||
FileName = FFprobePath,
|
FileName = FFprobePath,
|
||||||
Arguments = string.Format(args, probeSizeArgument, inputPath).Trim(),
|
Arguments = args,
|
||||||
|
|
||||||
|
|
||||||
IsHidden = true,
|
IsHidden = true,
|
||||||
ErrorDialog = false,
|
ErrorDialog = false,
|
||||||
|
@ -383,13 +384,20 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
|
|
||||||
using (var processWrapper = new ProcessWrapper(process, this, _logger))
|
using (var processWrapper = new ProcessWrapper(process, this, _logger))
|
||||||
{
|
{
|
||||||
|
_logger.LogDebug("Starting ffprobe with args {Args}", args);
|
||||||
StartProcess(processWrapper);
|
StartProcess(processWrapper);
|
||||||
|
|
||||||
|
InternalMediaInfoResult result;
|
||||||
try
|
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))
|
if (result == null || (result.streams == null && result.format == null))
|
||||||
{
|
{
|
||||||
|
@ -405,6 +413,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
{
|
{
|
||||||
stream.display_aspect_ratio = string.Empty;
|
stream.display_aspect_ratio = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.Equals(stream.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(stream.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
stream.sample_aspect_ratio = string.Empty;
|
stream.sample_aspect_ratio = string.Empty;
|
||||||
|
@ -414,13 +423,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
|
|
||||||
return new ProbeResultNormalizer(_logger, FileSystem).GetMediaInfo(result, videoType, isAudio, primaryPath, protocol);
|
return new ProbeResultNormalizer(_logger, FileSystem).GetMediaInfo(result, videoType, isAudio, primaryPath, protocol);
|
||||||
}
|
}
|
||||||
catch
|
|
||||||
{
|
|
||||||
StopProcess(processWrapper, 100);
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
private readonly ILocalizationManager _localization;
|
private readonly ILocalizationManager _localization;
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
|
|
||||||
private string[] SubtitleExtensions = new[]
|
private static readonly HashSet<string> SubtitleExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||||
{
|
{
|
||||||
".srt",
|
".srt",
|
||||||
".ssa",
|
".ssa",
|
||||||
|
@ -49,9 +49,16 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
|
|
||||||
startIndex += streams.Count;
|
startIndex += streams.Count;
|
||||||
|
|
||||||
|
string folder = video.GetInternalMetadataPath();
|
||||||
|
|
||||||
|
if (!Directory.Exists(folder))
|
||||||
|
{
|
||||||
|
return streams;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
AddExternalSubtitleStreams(streams, video.GetInternalMetadataPath(), video.Path, startIndex, directoryService, clearCache);
|
AddExternalSubtitleStreams(streams, folder, video.Path, startIndex, directoryService, clearCache);
|
||||||
}
|
}
|
||||||
catch (IOException)
|
catch (IOException)
|
||||||
{
|
{
|
||||||
|
@ -105,7 +112,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
{
|
{
|
||||||
var extension = Path.GetExtension(fullName);
|
var extension = Path.GetExtension(fullName);
|
||||||
|
|
||||||
if (!SubtitleExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
|
if (!SubtitleExtensions.Contains(extension))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
532
SocketHttpListener/Net/HttpConnection.cs
Normal file
532
SocketHttpListener/Net/HttpConnection.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user