diff options
| author | LukePulverenti <luke.pulverenti@gmail.com> | 2013-02-25 22:43:04 -0500 |
|---|---|---|
| committer | LukePulverenti <luke.pulverenti@gmail.com> | 2013-02-25 22:43:04 -0500 |
| commit | 2d06095447b972c8c7239277428e2c67c8b7ca86 (patch) | |
| tree | 14278bd4c0732ee962b73ff4845e5022e157a0a3 /MediaBrowser.Common/Net | |
| parent | 364fbb9e0c7586afa296ddd7d739df086f4c3533 (diff) | |
plugin security fixes and other abstractions
Diffstat (limited to 'MediaBrowser.Common/Net')
| -rw-r--r-- | MediaBrowser.Common/Net/BaseRestService.cs | 458 | ||||
| -rw-r--r-- | MediaBrowser.Common/Net/Handlers/BaseActionHandler.cs | 31 | ||||
| -rw-r--r-- | MediaBrowser.Common/Net/Handlers/BaseSerializationHandler.cs | 133 | ||||
| -rw-r--r-- | MediaBrowser.Common/Net/IHttpClient.cs | 8 | ||||
| -rw-r--r-- | MediaBrowser.Common/Net/IHttpServer.cs | 6 | ||||
| -rw-r--r-- | MediaBrowser.Common/Net/IRestfulService.cs | 2 | ||||
| -rw-r--r-- | MediaBrowser.Common/Net/WebSocketConnection.cs | 28 |
7 files changed, 25 insertions, 641 deletions
diff --git a/MediaBrowser.Common/Net/BaseRestService.cs b/MediaBrowser.Common/Net/BaseRestService.cs deleted file mode 100644 index a7e95fca2..000000000 --- a/MediaBrowser.Common/Net/BaseRestService.cs +++ /dev/null @@ -1,458 +0,0 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.IO; -using MediaBrowser.Common.Kernel; -using MediaBrowser.Model.Logging; -using ServiceStack.Common; -using ServiceStack.Common.Web; -using ServiceStack.ServiceHost; -using ServiceStack.ServiceInterface; -using ServiceStack.WebHost.Endpoints; -using System; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Net; -using System.Threading.Tasks; - -namespace MediaBrowser.Common.Net -{ - /// <summary> - /// Class BaseRestService - /// </summary> - public class BaseRestService : Service, IRestfulService - { - /// <summary> - /// Gets or sets the kernel. - /// </summary> - /// <value>The kernel.</value> - public IKernel Kernel { get; set; } - - /// <summary> - /// Gets or sets the logger. - /// </summary> - /// <value>The logger.</value> - public ILogger Logger { get; set; } - - /// <summary> - /// Gets a value indicating whether this instance is range request. - /// </summary> - /// <value><c>true</c> if this instance is range request; otherwise, <c>false</c>.</value> - protected bool IsRangeRequest - { - get - { - return Request.Headers.AllKeys.Contains("Range"); - } - } - - /// <summary> - /// Adds the routes. - /// </summary> - /// <param name="appHost">The app host.</param> - public virtual void Configure(IAppHost appHost) - { - } - - /// <summary> - /// To the optimized result. - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="result">The result.</param> - /// <returns>System.Object.</returns> - /// <exception cref="System.ArgumentNullException">result</exception> - protected object ToOptimizedResult<T>(T result) - where T : class - { - if (result == null) - { - throw new ArgumentNullException("result"); - } - - Response.AddHeader("Vary", "Accept-Encoding"); - - return RequestContext.ToOptimizedResult(result); - } - - /// <summary> - /// To the optimized result using cache. - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="cacheKey">The cache key.</param> - /// <param name="lastDateModified">The last date modified.</param> - /// <param name="cacheDuration">Duration of the cache.</param> - /// <param name="factoryFn">The factory fn.</param> - /// <returns>System.Object.</returns> - /// <exception cref="System.ArgumentNullException">cacheKey</exception> - protected object ToOptimizedResultUsingCache<T>(Guid cacheKey, DateTime lastDateModified, TimeSpan? cacheDuration, Func<T> factoryFn) - where T : class - { - if (cacheKey == Guid.Empty) - { - throw new ArgumentNullException("cacheKey"); - } - if (factoryFn == null) - { - throw new ArgumentNullException("factoryFn"); - } - - var key = cacheKey.ToString("N"); - - var result = PreProcessCachedResult(cacheKey, key, lastDateModified, cacheDuration, string.Empty); - - if (result != null) - { - return result; - } - - return ToOptimizedResult(factoryFn()); - } - - /// <summary> - /// To the cached result. - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="cacheKey">The cache key.</param> - /// <param name="lastDateModified">The last date modified.</param> - /// <param name="cacheDuration">Duration of the cache.</param> - /// <param name="factoryFn">The factory fn.</param> - /// <param name="contentType">Type of the content.</param> - /// <returns>System.Object.</returns> - /// <exception cref="System.ArgumentNullException">cacheKey</exception> - protected object ToCachedResult<T>(Guid cacheKey, DateTime lastDateModified, TimeSpan? cacheDuration, Func<T> factoryFn, string contentType) - where T : class - { - if (cacheKey == Guid.Empty) - { - throw new ArgumentNullException("cacheKey"); - } - if (factoryFn == null) - { - throw new ArgumentNullException("factoryFn"); - } - - var key = cacheKey.ToString("N"); - - var result = PreProcessCachedResult(cacheKey, key, lastDateModified, cacheDuration, contentType); - - if (result != null) - { - return result; - } - - return factoryFn(); - } - - /// <summary> - /// To the static file result. - /// </summary> - /// <param name="path">The path.</param> - /// <returns>System.Object.</returns> - /// <exception cref="System.ArgumentNullException">path</exception> - protected object ToStaticFileResult(string path) - { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException("path"); - } - - var dateModified = File.GetLastWriteTimeUtc(path); - - var cacheKey = path + dateModified.Ticks; - - return ToStaticResult(cacheKey.GetMD5(), dateModified, null, MimeTypes.GetMimeType(path), () => Task.FromResult(GetFileStream(path))); - } - - /// <summary> - /// Gets the file stream. - /// </summary> - /// <param name="path">The path.</param> - /// <returns>Stream.</returns> - private Stream GetFileStream(string path) - { - return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous); - } - - /// <summary> - /// To the static result. - /// </summary> - /// <param name="cacheKey">The cache key.</param> - /// <param name="lastDateModified">The last date modified.</param> - /// <param name="cacheDuration">Duration of the cache.</param> - /// <param name="contentType">Type of the content.</param> - /// <param name="factoryFn">The factory fn.</param> - /// <returns>System.Object.</returns> - /// <exception cref="System.ArgumentNullException">cacheKey</exception> - protected object ToStaticResult(Guid cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration, string contentType, Func<Task<Stream>> factoryFn) - { - if (cacheKey == Guid.Empty) - { - throw new ArgumentNullException("cacheKey"); - } - if (factoryFn == null) - { - throw new ArgumentNullException("factoryFn"); - } - - var key = cacheKey.ToString("N"); - - var result = PreProcessCachedResult(cacheKey, key, lastDateModified, cacheDuration, contentType); - - if (result != null) - { - return result; - } - - var compress = ShouldCompressResponse(contentType); - - if (compress) - { - Response.AddHeader("Vary", "Accept-Encoding"); - } - - return ToStaticResult(contentType, factoryFn, compress).Result; - } - - /// <summary> - /// Shoulds the compress response. - /// </summary> - /// <param name="contentType">Type of the content.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - private bool ShouldCompressResponse(string contentType) - { - // It will take some work to support compression with byte range requests - if (IsRangeRequest) - { - return false; - } - - // Don't compress media - if (contentType.StartsWith("audio/", StringComparison.OrdinalIgnoreCase) || contentType.StartsWith("video/", StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - // Don't compress images - if (contentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - if (contentType.StartsWith("font/", StringComparison.OrdinalIgnoreCase)) - { - return false; - } - if (contentType.StartsWith("application/", StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - return true; - } - - /// <summary> - /// To the static result. - /// </summary> - /// <param name="contentType">Type of the content.</param> - /// <param name="factoryFn">The factory fn.</param> - /// <param name="compress">if set to <c>true</c> [compress].</param> - /// <returns>System.Object.</returns> - private async Task<object> ToStaticResult(string contentType, Func<Task<Stream>> factoryFn, bool compress) - { - if (!compress || string.IsNullOrEmpty(RequestContext.CompressionType)) - { - Response.ContentType = contentType; - - var stream = await factoryFn().ConfigureAwait(false); - - return new StreamWriter(stream); - } - - string content; - - using (var stream = await factoryFn().ConfigureAwait(false)) - { - using (var reader = new StreamReader(stream)) - { - content = await reader.ReadToEndAsync().ConfigureAwait(false); - } - } - - var contents = content.Compress(RequestContext.CompressionType); - - return new CompressedResult(contents, RequestContext.CompressionType, contentType); - } - - /// <summary> - /// Pres the process optimized result. - /// </summary> - /// <param name="cacheKey">The cache key.</param> - /// <param name="cacheKeyString">The cache key string.</param> - /// <param name="lastDateModified">The last date modified.</param> - /// <param name="cacheDuration">Duration of the cache.</param> - /// <param name="contentType">Type of the content.</param> - /// <returns>System.Object.</returns> - private object PreProcessCachedResult(Guid cacheKey, string cacheKeyString, DateTime? lastDateModified, TimeSpan? cacheDuration, string contentType) - { - Response.AddHeader("ETag", cacheKeyString); - - if (IsNotModified(cacheKey, lastDateModified, cacheDuration)) - { - AddAgeHeader(lastDateModified); - AddExpiresHeader(cacheKeyString, cacheDuration); - //ctx.Response.SendChunked = false; - - if (!string.IsNullOrEmpty(contentType)) - { - Response.ContentType = contentType; - } - - return new HttpResult(new byte[] { }, HttpStatusCode.NotModified); - } - - SetCachingHeaders(cacheKeyString, lastDateModified, cacheDuration); - - return null; - } - - /// <summary> - /// Determines whether [is not modified] [the specified cache key]. - /// </summary> - /// <param name="cacheKey">The cache key.</param> - /// <param name="lastDateModified">The last date modified.</param> - /// <param name="cacheDuration">Duration of the cache.</param> - /// <returns><c>true</c> if [is not modified] [the specified cache key]; otherwise, <c>false</c>.</returns> - private bool IsNotModified(Guid? cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration) - { - var isNotModified = true; - - if (Request.Headers.AllKeys.Contains("If-Modified-Since")) - { - DateTime ifModifiedSince; - - if (DateTime.TryParse(Request.Headers["If-Modified-Since"], out ifModifiedSince)) - { - isNotModified = IsNotModified(ifModifiedSince.ToUniversalTime(), cacheDuration, lastDateModified); - } - } - - // Validate If-None-Match - if (isNotModified && (cacheKey.HasValue || !string.IsNullOrEmpty(Request.Headers["If-None-Match"]))) - { - Guid ifNoneMatch; - - if (Guid.TryParse(Request.Headers["If-None-Match"] ?? string.Empty, out ifNoneMatch)) - { - if (cacheKey.HasValue && cacheKey.Value == ifNoneMatch) - { - return true; - } - } - } - - return false; - } - - /// <summary> - /// Determines whether [is not modified] [the specified if modified since]. - /// </summary> - /// <param name="ifModifiedSince">If modified since.</param> - /// <param name="cacheDuration">Duration of the cache.</param> - /// <param name="dateModified">The date modified.</param> - /// <returns><c>true</c> if [is not modified] [the specified if modified since]; otherwise, <c>false</c>.</returns> - private bool IsNotModified(DateTime ifModifiedSince, TimeSpan? cacheDuration, DateTime? dateModified) - { - if (dateModified.HasValue) - { - var lastModified = NormalizeDateForComparison(dateModified.Value); - ifModifiedSince = NormalizeDateForComparison(ifModifiedSince); - - return lastModified <= ifModifiedSince; - } - - if (cacheDuration.HasValue) - { - var cacheExpirationDate = ifModifiedSince.Add(cacheDuration.Value); - - if (DateTime.UtcNow < cacheExpirationDate) - { - return true; - } - } - - return false; - } - - - /// <summary> - /// When the browser sends the IfModifiedDate, it's precision is limited to seconds, so this will account for that - /// </summary> - /// <param name="date">The date.</param> - /// <returns>DateTime.</returns> - private DateTime NormalizeDateForComparison(DateTime date) - { - return new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, date.Kind); - } - - /// <summary> - /// Sets the caching headers. - /// </summary> - /// <param name="cacheKey">The cache key.</param> - /// <param name="lastDateModified">The last date modified.</param> - /// <param name="cacheDuration">Duration of the cache.</param> - private void SetCachingHeaders(string cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration) - { - // Don't specify both last modified and Etag, unless caching unconditionally. They are redundant - // https://developers.google.com/speed/docs/best-practices/caching#LeverageBrowserCaching - if (lastDateModified.HasValue && (string.IsNullOrEmpty(cacheKey) || cacheDuration.HasValue)) - { - AddAgeHeader(lastDateModified); - Response.AddHeader("LastModified", lastDateModified.Value.ToString("r")); - } - - if (cacheDuration.HasValue) - { - Response.AddHeader("Cache-Control", "public, max-age=" + Convert.ToInt32(cacheDuration.Value.TotalSeconds)); - } - else if (!string.IsNullOrEmpty(cacheKey)) - { - Response.AddHeader("Cache-Control", "public"); - } - else - { - Response.AddHeader("Cache-Control", "no-cache, no-store, must-revalidate"); - Response.AddHeader("pragma", "no-cache, no-store, must-revalidate"); - } - - AddExpiresHeader(cacheKey, cacheDuration); - } - - /// <summary> - /// Adds the expires header. - /// </summary> - /// <param name="cacheKey">The cache key.</param> - /// <param name="cacheDuration">Duration of the cache.</param> - private void AddExpiresHeader(string cacheKey, TimeSpan? cacheDuration) - { - if (cacheDuration.HasValue) - { - Response.AddHeader("Expires", DateTime.UtcNow.Add(cacheDuration.Value).ToString("r")); - } - else if (string.IsNullOrEmpty(cacheKey)) - { - Response.AddHeader("Expires", "-1"); - } - } - - /// <summary> - /// Adds the age header. - /// </summary> - /// <param name="lastDateModified">The last date modified.</param> - private void AddAgeHeader(DateTime? lastDateModified) - { - if (lastDateModified.HasValue) - { - Response.AddHeader("Age", Convert.ToInt64((DateTime.UtcNow - lastDateModified.Value).TotalSeconds).ToString(CultureInfo.InvariantCulture)); - } - } - } -} diff --git a/MediaBrowser.Common/Net/Handlers/BaseActionHandler.cs b/MediaBrowser.Common/Net/Handlers/BaseActionHandler.cs deleted file mode 100644 index 72df88519..000000000 --- a/MediaBrowser.Common/Net/Handlers/BaseActionHandler.cs +++ /dev/null @@ -1,31 +0,0 @@ -using MediaBrowser.Common.Kernel; -using MediaBrowser.Model.Entities; -using System.Threading.Tasks; - -namespace MediaBrowser.Common.Net.Handlers -{ - /// <summary> - /// Class BaseActionHandler - /// </summary> - /// <typeparam name="TKernelType">The type of the T kernel type.</typeparam> - public abstract class BaseActionHandler<TKernelType> : BaseSerializationHandler<TKernelType, EmptyRequestResult> - where TKernelType : IKernel - { - /// <summary> - /// Gets the object to serialize. - /// </summary> - /// <returns>Task{EmptyRequestResult}.</returns> - protected override async Task<EmptyRequestResult> GetObjectToSerialize() - { - await ExecuteAction(); - - return new EmptyRequestResult(); - } - - /// <summary> - /// Performs the action. - /// </summary> - /// <returns>Task.</returns> - protected abstract Task ExecuteAction(); - } -} diff --git a/MediaBrowser.Common/Net/Handlers/BaseSerializationHandler.cs b/MediaBrowser.Common/Net/Handlers/BaseSerializationHandler.cs deleted file mode 100644 index a00152d78..000000000 --- a/MediaBrowser.Common/Net/Handlers/BaseSerializationHandler.cs +++ /dev/null @@ -1,133 +0,0 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Kernel; -using System; -using System.IO; -using System.Threading.Tasks; - -namespace MediaBrowser.Common.Net.Handlers -{ - /// <summary> - /// Class BaseSerializationHandler - /// </summary> - /// <typeparam name="TKernelType">The type of the T kernel type.</typeparam> - /// <typeparam name="T"></typeparam> - public abstract class BaseSerializationHandler<TKernelType, T> : BaseHandler<TKernelType> - where TKernelType : IKernel - where T : class - { - /// <summary> - /// Gets the serialization format. - /// </summary> - /// <value>The serialization format.</value> - public SerializationFormat SerializationFormat - { - get - { - var format = QueryString["dataformat"]; - - if (string.IsNullOrEmpty(format)) - { - return SerializationFormat.Json; - } - - return (SerializationFormat)Enum.Parse(typeof(SerializationFormat), format, true); - } - } - - /// <summary> - /// Gets the type of the content. - /// </summary> - /// <value>The type of the content.</value> - protected string ContentType - { - get - { - switch (SerializationFormat) - { - case SerializationFormat.Protobuf: - return "application/x-protobuf"; - default: - return MimeTypes.JsonMimeType; - } - } - } - - /// <summary> - /// Gets the response info. - /// </summary> - /// <returns>Task{ResponseInfo}.</returns> - protected override Task<ResponseInfo> GetResponseInfo() - { - return Task.FromResult(new ResponseInfo - { - ContentType = ContentType - }); - } - - /// <summary> - /// Called when [processing request]. - /// </summary> - /// <param name="responseInfo">The response info.</param> - /// <returns>Task.</returns> - protected override async Task OnProcessingRequest(ResponseInfo responseInfo) - { - _objectToSerialize = await GetObjectToSerialize().ConfigureAwait(false); - - if (_objectToSerialize == null) - { - throw new ResourceNotFoundException(); - } - - await base.OnProcessingRequest(responseInfo).ConfigureAwait(false); - } - - /// <summary> - /// The _object to serialize - /// </summary> - private T _objectToSerialize; - - /// <summary> - /// Gets the object to serialize. - /// </summary> - /// <returns>Task{`0}.</returns> - protected abstract Task<T> GetObjectToSerialize(); - - /// <summary> - /// Writes the response to output stream. - /// </summary> - /// <param name="stream">The stream.</param> - /// <param name="responseInfo">The response info.</param> - /// <param name="contentLength">Length of the content.</param> - /// <returns>Task.</returns> - protected override Task WriteResponseToOutputStream(Stream stream, ResponseInfo responseInfo, long? contentLength) - { - return Task.Run(() => - { - //switch (SerializationFormat) - //{ - // case SerializationFormat.Protobuf: - // Kernel.ProtobufSerializer.SerializeToStream(_objectToSerialize, stream); - // break; - // default: - // JsonSerializer.SerializeToStream(_objectToSerialize, stream); - // break; - //} - }); - } - } - - /// <summary> - /// Enum SerializationFormat - /// </summary> - public enum SerializationFormat - { - /// <summary> - /// The json - /// </summary> - Json, - /// <summary> - /// The protobuf - /// </summary> - Protobuf - } -} diff --git a/MediaBrowser.Common/Net/IHttpClient.cs b/MediaBrowser.Common/Net/IHttpClient.cs index ef0dd69b7..d02f08572 100644 --- a/MediaBrowser.Common/Net/IHttpClient.cs +++ b/MediaBrowser.Common/Net/IHttpClient.cs @@ -6,6 +6,9 @@ using System.Threading.Tasks; namespace MediaBrowser.Common.Net { + /// <summary> + /// Interface IHttpClient + /// </summary> public interface IHttpClient : IDisposable { /// <summary> @@ -52,10 +55,5 @@ namespace MediaBrowser.Common.Net /// <returns>Task{MemoryStream}.</returns> /// <exception cref="MediaBrowser.Model.Net.HttpException"></exception> Task<MemoryStream> GetMemoryStream(string url, SemaphoreSlim resourcePool, CancellationToken cancellationToken); - - /// <summary> - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// </summary> - void Dispose(); } }
\ No newline at end of file diff --git a/MediaBrowser.Common/Net/IHttpServer.cs b/MediaBrowser.Common/Net/IHttpServer.cs index a640fb262..45da18e1b 100644 --- a/MediaBrowser.Common/Net/IHttpServer.cs +++ b/MediaBrowser.Common/Net/IHttpServer.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace MediaBrowser.Common.Net { @@ -40,5 +41,10 @@ namespace MediaBrowser.Common.Net /// Occurs when [web socket connected]. /// </summary> event EventHandler<WebSocketConnectEventArgs> WebSocketConnected; + + /// <summary> + /// Inits this instance. + /// </summary> + void Init(IEnumerable<IRestfulService> services); } }
\ No newline at end of file diff --git a/MediaBrowser.Common/Net/IRestfulService.cs b/MediaBrowser.Common/Net/IRestfulService.cs index 7fc6bb61e..36055cd8a 100644 --- a/MediaBrowser.Common/Net/IRestfulService.cs +++ b/MediaBrowser.Common/Net/IRestfulService.cs @@ -1,5 +1,4 @@ using ServiceStack.ServiceHost; -using ServiceStack.WebHost.Endpoints; using System; namespace MediaBrowser.Common.Net @@ -9,6 +8,5 @@ namespace MediaBrowser.Common.Net /// </summary> public interface IRestfulService : IService, IRequiresRequestContext, IDisposable { - void Configure(IAppHost appHost); } } diff --git a/MediaBrowser.Common/Net/WebSocketConnection.cs b/MediaBrowser.Common/Net/WebSocketConnection.cs index 36d649e3b..1ad0e8f06 100644 --- a/MediaBrowser.Common/Net/WebSocketConnection.cs +++ b/MediaBrowser.Common/Net/WebSocketConnection.cs @@ -41,17 +41,22 @@ namespace MediaBrowser.Common.Net /// The _json serializer /// </summary> private readonly IJsonSerializer _jsonSerializer; - + + /// <summary> + /// Gets or sets the receive action. + /// </summary> + /// <value>The receive action.</value> + public Action<WebSocketMessageInfo> OnReceive { get; set; } + /// <summary> /// Initializes a new instance of the <see cref="WebSocketConnection" /> class. /// </summary> /// <param name="socket">The socket.</param> /// <param name="remoteEndPoint">The remote end point.</param> - /// <param name="receiveAction">The receive action.</param> /// <param name="jsonSerializer">The json serializer.</param> /// <param name="logger">The logger.</param> /// <exception cref="System.ArgumentNullException">socket</exception> - public WebSocketConnection(IWebSocket socket, string remoteEndPoint, Action<WebSocketMessageInfo> receiveAction, IJsonSerializer jsonSerializer, ILogger logger) + public WebSocketConnection(IWebSocket socket, string remoteEndPoint, IJsonSerializer jsonSerializer, ILogger logger) { if (socket == null) { @@ -61,10 +66,6 @@ namespace MediaBrowser.Common.Net { throw new ArgumentNullException("remoteEndPoint"); } - if (receiveAction == null) - { - throw new ArgumentNullException("receiveAction"); - } if (jsonSerializer == null) { throw new ArgumentNullException("jsonSerializer"); @@ -76,7 +77,7 @@ namespace MediaBrowser.Common.Net _jsonSerializer = jsonSerializer; _socket = socket; - _socket.OnReceiveDelegate = info => OnReceive(info, receiveAction); + _socket.OnReceiveDelegate = OnReceiveInternal; RemoteEndPoint = remoteEndPoint; _logger = logger; } @@ -85,21 +86,24 @@ namespace MediaBrowser.Common.Net /// Called when [receive]. /// </summary> /// <param name="bytes">The bytes.</param> - /// <param name="callback">The callback.</param> - private void OnReceive(byte[] bytes, Action<WebSocketMessageInfo> callback) + private void OnReceiveInternal(byte[] bytes) { + if (OnReceive == null) + { + return; + } try { WebSocketMessageInfo info; using (var memoryStream = new MemoryStream(bytes)) { - info = _jsonSerializer.DeserializeFromStream<WebSocketMessageInfo>(memoryStream); + info = (WebSocketMessageInfo)_jsonSerializer.DeserializeFromStream(memoryStream, typeof(WebSocketMessageInfo)); } info.Connection = this; - callback(info); + OnReceive(info); } catch (Exception ex) { |
