aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller/Net
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Controller/Net')
-rw-r--r--MediaBrowser.Controller/Net/AuthenticatedAttribute.cs69
-rw-r--r--MediaBrowser.Controller/Net/AuthorizationInfo.cs52
-rw-r--r--MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs322
-rw-r--r--MediaBrowser.Controller/Net/IAuthService.cs9
-rw-r--r--MediaBrowser.Controller/Net/IAuthorizationContext.cs21
-rw-r--r--MediaBrowser.Controller/Net/IHasResultFactory.cs17
-rw-r--r--MediaBrowser.Controller/Net/IHttpResultFactory.cs80
-rw-r--r--MediaBrowser.Controller/Net/IHttpServer.cs39
-rw-r--r--MediaBrowser.Controller/Net/ISessionContext.cs16
-rw-r--r--MediaBrowser.Controller/Net/IWebSocketConnection.cs85
-rw-r--r--MediaBrowser.Controller/Net/IWebSocketListener.cs17
-rw-r--r--MediaBrowser.Controller/Net/SecurityException.cs21
-rw-r--r--MediaBrowser.Controller/Net/StaticResultOptions.cs41
-rw-r--r--MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs41
-rw-r--r--MediaBrowser.Controller/Net/WebSocketMessageInfo.cs16
15 files changed, 846 insertions, 0 deletions
diff --git a/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs b/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs
new file mode 100644
index 000000000..2f31b8e66
--- /dev/null
+++ b/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs
@@ -0,0 +1,69 @@
+using System;
+using MediaBrowser.Model.Services;
+
+namespace MediaBrowser.Controller.Net
+{
+ public class AuthenticatedAttribute : Attribute, IHasRequestFilter, IAuthenticationAttributes
+ {
+ public static IAuthService AuthService { get; set; }
+
+ /// <summary>
+ /// Gets or sets the roles.
+ /// </summary>
+ /// <value>The roles.</value>
+ public string Roles { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether [escape parental control].
+ /// </summary>
+ /// <value><c>true</c> if [escape parental control]; otherwise, <c>false</c>.</value>
+ public bool EscapeParentalControl { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether [allow before startup wizard].
+ /// </summary>
+ /// <value><c>true</c> if [allow before startup wizard]; otherwise, <c>false</c>.</value>
+ public bool AllowBeforeStartupWizard { get; set; }
+
+ public bool AllowLocal { get; set; }
+
+ /// <summary>
+ /// The request filter is executed before the service.
+ /// </summary>
+ /// <param name="request">The http request wrapper</param>
+ /// <param name="response">The http response wrapper</param>
+ /// <param name="requestDto">The request DTO</param>
+ public void RequestFilter(IRequest request, IResponse response, object requestDto)
+ {
+ AuthService.Authenticate(request, this);
+ }
+
+ /// <summary>
+ /// Order in which Request Filters are executed.
+ /// &lt;0 Executed before global request filters
+ /// &gt;0 Executed after global request filters
+ /// </summary>
+ /// <value>The priority.</value>
+ public int Priority
+ {
+ get { return 0; }
+ }
+
+ public string[] GetRoles()
+ {
+ return (Roles ?? string.Empty).Split(new []{ ',' }, StringSplitOptions.RemoveEmptyEntries);
+ }
+
+ public bool AllowLocalOnly { get; set; }
+ }
+
+ public interface IAuthenticationAttributes
+ {
+ bool EscapeParentalControl { get; }
+ bool AllowBeforeStartupWizard { get; }
+ bool AllowLocal { get; }
+ bool AllowLocalOnly { get; }
+
+ string[] GetRoles();
+ }
+}
diff --git a/MediaBrowser.Controller/Net/AuthorizationInfo.cs b/MediaBrowser.Controller/Net/AuthorizationInfo.cs
new file mode 100644
index 000000000..a68060db5
--- /dev/null
+++ b/MediaBrowser.Controller/Net/AuthorizationInfo.cs
@@ -0,0 +1,52 @@
+using MediaBrowser.Controller.Entities;
+using System;
+
+
+namespace MediaBrowser.Controller.Net
+{
+ public class AuthorizationInfo
+ {
+ /// <summary>
+ /// Gets or sets the user identifier.
+ /// </summary>
+ /// <value>The user identifier.</value>
+ public Guid UserId {
+ get {
+ if (User == null) {
+ return Guid.Empty;
+ }
+ else {
+ return User.Id;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the device identifier.
+ /// </summary>
+ /// <value>The device identifier.</value>
+ public string DeviceId { get; set; }
+ /// <summary>
+ /// Gets or sets the device.
+ /// </summary>
+ /// <value>The device.</value>
+ public string Device { get; set; }
+ /// <summary>
+ /// Gets or sets the client.
+ /// </summary>
+ /// <value>The client.</value>
+ public string Client { get; set; }
+ /// <summary>
+ /// Gets or sets the version.
+ /// </summary>
+ /// <value>The version.</value>
+ public string Version { get; set; }
+ /// <summary>
+ /// Gets or sets the token.
+ /// </summary>
+ /// <value>The token.</value>
+ public string Token { get; set; }
+
+ public User User { get; set; }
+ }
+}
diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
new file mode 100644
index 000000000..7df96b777
--- /dev/null
+++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
@@ -0,0 +1,322 @@
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Threading;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Net.WebSockets;
+using System.Threading.Tasks;
+using System.Threading;
+using System;
+
+namespace MediaBrowser.Controller.Net
+{
+ /// <summary>
+ /// Starts sending data over a web socket periodically when a message is received, and then stops when a corresponding stop message is received
+ /// </summary>
+ /// <typeparam name="TReturnDataType">The type of the T return data type.</typeparam>
+ /// <typeparam name="TStateType">The type of the T state type.</typeparam>
+ public abstract class BasePeriodicWebSocketListener<TReturnDataType, TStateType> : IWebSocketListener, IDisposable
+ where TStateType : WebSocketListenerState, new()
+ where TReturnDataType : class
+ {
+ /// <summary>
+ /// The _active connections
+ /// </summary>
+ protected readonly List<Tuple<IWebSocketConnection, CancellationTokenSource, ITimer, TStateType>> ActiveConnections =
+ new List<Tuple<IWebSocketConnection, CancellationTokenSource, ITimer, TStateType>>();
+
+ /// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ protected abstract string Name { get; }
+
+ /// <summary>
+ /// Gets the data to send.
+ /// </summary>
+ /// <param name="state">The state.</param>
+ /// <returns>Task{`1}.</returns>
+ protected abstract Task<TReturnDataType> GetDataToSend(TStateType state, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// The logger
+ /// </summary>
+ protected ILogger Logger;
+
+ protected ITimerFactory TimerFactory { get; private set; }
+
+ protected BasePeriodicWebSocketListener(ILogger logger)
+ {
+ if (logger == null)
+ {
+ throw new ArgumentNullException("logger");
+ }
+
+ Logger = logger;
+ }
+
+ /// <summary>
+ /// Processes the message.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <returns>Task.</returns>
+ public Task ProcessMessage(WebSocketMessageInfo message)
+ {
+ if (message == null)
+ {
+ throw new ArgumentNullException("message");
+ }
+
+ if (string.Equals(message.MessageType, Name + "Start", StringComparison.OrdinalIgnoreCase))
+ {
+ Start(message);
+ }
+
+ if (string.Equals(message.MessageType, Name + "Stop", StringComparison.OrdinalIgnoreCase))
+ {
+ Stop(message);
+ }
+
+ return Task.FromResult(true);
+ }
+
+ protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
+
+ protected virtual bool SendOnTimer
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ protected virtual void ParseMessageParams(string[] values)
+ {
+
+ }
+
+ /// <summary>
+ /// Starts sending messages over a web socket
+ /// </summary>
+ /// <param name="message">The message.</param>
+ private void Start(WebSocketMessageInfo message)
+ {
+ var vals = message.Data.Split(',');
+
+ var dueTimeMs = long.Parse(vals[0], UsCulture);
+ var periodMs = long.Parse(vals[1], UsCulture);
+
+ if (vals.Length > 2)
+ {
+ ParseMessageParams(vals.Skip(2).ToArray());
+ }
+
+ var cancellationTokenSource = new CancellationTokenSource();
+
+ Logger.Debug("{1} Begin transmitting over websocket to {0}", message.Connection.RemoteEndPoint, GetType().Name);
+
+ var timer = SendOnTimer ?
+ TimerFactory.Create(TimerCallback, message.Connection, Timeout.Infinite, Timeout.Infinite) :
+ null;
+
+ var state = new TStateType
+ {
+ IntervalMs = periodMs,
+ InitialDelayMs = dueTimeMs
+ };
+
+ lock (ActiveConnections)
+ {
+ ActiveConnections.Add(new Tuple<IWebSocketConnection, CancellationTokenSource, ITimer, TStateType>(message.Connection, cancellationTokenSource, timer, state));
+ }
+
+ if (timer != null)
+ {
+ timer.Change(TimeSpan.FromMilliseconds(dueTimeMs), TimeSpan.FromMilliseconds(periodMs));
+ }
+ }
+
+ /// <summary>
+ /// Timers the callback.
+ /// </summary>
+ /// <param name="state">The state.</param>
+ private void TimerCallback(object state)
+ {
+ var connection = (IWebSocketConnection)state;
+
+ Tuple<IWebSocketConnection, CancellationTokenSource, ITimer, TStateType> tuple;
+
+ lock (ActiveConnections)
+ {
+ tuple = ActiveConnections.FirstOrDefault(c => c.Item1 == connection);
+ }
+
+ if (tuple == null)
+ {
+ return;
+ }
+
+ if (connection.State != WebSocketState.Open || tuple.Item2.IsCancellationRequested)
+ {
+ DisposeConnection(tuple);
+ return;
+ }
+
+ SendData(tuple);
+ }
+
+ protected void SendData(bool force)
+ {
+ Tuple<IWebSocketConnection, CancellationTokenSource, ITimer, TStateType>[] tuples;
+
+ lock (ActiveConnections)
+ {
+ tuples = ActiveConnections
+ .Where(c =>
+ {
+ if (c.Item1.State == WebSocketState.Open && !c.Item2.IsCancellationRequested)
+ {
+ var state = c.Item4;
+
+ if (force || (DateTime.UtcNow - state.DateLastSendUtc).TotalMilliseconds >= state.IntervalMs)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ })
+ .ToArray();
+ }
+
+ foreach (var tuple in tuples)
+ {
+ SendData(tuple);
+ }
+ }
+
+ private async void SendData(Tuple<IWebSocketConnection, CancellationTokenSource, ITimer, TStateType> tuple)
+ {
+ var connection = tuple.Item1;
+
+ try
+ {
+ var state = tuple.Item4;
+
+ var cancellationToken = tuple.Item2.Token;
+
+ var data = await GetDataToSend(state, cancellationToken).ConfigureAwait(false);
+
+ if (data != null)
+ {
+ await connection.SendAsync(new WebSocketMessage<TReturnDataType>
+ {
+ MessageType = Name,
+ Data = data
+
+ }, cancellationToken).ConfigureAwait(false);
+
+ state.DateLastSendUtc = DateTime.UtcNow;
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ if (tuple.Item2.IsCancellationRequested)
+ {
+ DisposeConnection(tuple);
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error sending web socket message {0}", ex, Name);
+ DisposeConnection(tuple);
+ }
+ }
+
+ /// <summary>
+ /// Stops sending messages over a web socket
+ /// </summary>
+ /// <param name="message">The message.</param>
+ private void Stop(WebSocketMessageInfo message)
+ {
+ lock (ActiveConnections)
+ {
+ var connection = ActiveConnections.FirstOrDefault(c => c.Item1 == message.Connection);
+
+ if (connection != null)
+ {
+ DisposeConnection(connection);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Disposes the connection.
+ /// </summary>
+ /// <param name="connection">The connection.</param>
+ private void DisposeConnection(Tuple<IWebSocketConnection, CancellationTokenSource, ITimer, TStateType> connection)
+ {
+ Logger.Debug("{1} stop transmitting over websocket to {0}", connection.Item1.RemoteEndPoint, GetType().Name);
+
+ var timer = connection.Item3;
+
+ if (timer != null)
+ {
+ try
+ {
+ timer.Dispose();
+ }
+ catch (ObjectDisposedException)
+ {
+
+ }
+ }
+
+ try
+ {
+ connection.Item2.Cancel();
+ connection.Item2.Dispose();
+ }
+ catch (ObjectDisposedException)
+ {
+
+ }
+
+ ActiveConnections.Remove(connection);
+ }
+
+ /// <summary>
+ /// Releases unmanaged and - optionally - managed resources.
+ /// </summary>
+ /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected virtual void Dispose(bool dispose)
+ {
+ if (dispose)
+ {
+ lock (ActiveConnections)
+ {
+ foreach (var connection in ActiveConnections.ToArray())
+ {
+ DisposeConnection(connection);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ /// </summary>
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+ }
+
+ public class WebSocketListenerState
+ {
+ public DateTime DateLastSendUtc { get; set; }
+ public long InitialDelayMs { get; set; }
+ public long IntervalMs { get; set; }
+ }
+}
diff --git a/MediaBrowser.Controller/Net/IAuthService.cs b/MediaBrowser.Controller/Net/IAuthService.cs
new file mode 100644
index 000000000..361320250
--- /dev/null
+++ b/MediaBrowser.Controller/Net/IAuthService.cs
@@ -0,0 +1,9 @@
+using MediaBrowser.Model.Services;
+
+namespace MediaBrowser.Controller.Net
+{
+ public interface IAuthService
+ {
+ void Authenticate(IRequest request, IAuthenticationAttributes authAttribtues);
+ }
+}
diff --git a/MediaBrowser.Controller/Net/IAuthorizationContext.cs b/MediaBrowser.Controller/Net/IAuthorizationContext.cs
new file mode 100644
index 000000000..5a9d0aa30
--- /dev/null
+++ b/MediaBrowser.Controller/Net/IAuthorizationContext.cs
@@ -0,0 +1,21 @@
+using MediaBrowser.Model.Services;
+
+namespace MediaBrowser.Controller.Net
+{
+ public interface IAuthorizationContext
+ {
+ /// <summary>
+ /// Gets the authorization information.
+ /// </summary>
+ /// <param name="requestContext">The request context.</param>
+ /// <returns>AuthorizationInfo.</returns>
+ AuthorizationInfo GetAuthorizationInfo(object requestContext);
+
+ /// <summary>
+ /// Gets the authorization information.
+ /// </summary>
+ /// <param name="requestContext">The request context.</param>
+ /// <returns>AuthorizationInfo.</returns>
+ AuthorizationInfo GetAuthorizationInfo(IRequest requestContext);
+ }
+}
diff --git a/MediaBrowser.Controller/Net/IHasResultFactory.cs b/MediaBrowser.Controller/Net/IHasResultFactory.cs
new file mode 100644
index 000000000..03144e4b8
--- /dev/null
+++ b/MediaBrowser.Controller/Net/IHasResultFactory.cs
@@ -0,0 +1,17 @@
+using MediaBrowser.Model.Services;
+
+namespace MediaBrowser.Controller.Net
+{
+ /// <summary>
+ /// Interface IHasResultFactory
+ /// Services that require a ResultFactory should implement this
+ /// </summary>
+ public interface IHasResultFactory : IRequiresRequest
+ {
+ /// <summary>
+ /// Gets or sets the result factory.
+ /// </summary>
+ /// <value>The result factory.</value>
+ IHttpResultFactory ResultFactory { get; set; }
+ }
+}
diff --git a/MediaBrowser.Controller/Net/IHttpResultFactory.cs b/MediaBrowser.Controller/Net/IHttpResultFactory.cs
new file mode 100644
index 000000000..f8e631de3
--- /dev/null
+++ b/MediaBrowser.Controller/Net/IHttpResultFactory.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Services;
+
+namespace MediaBrowser.Controller.Net
+{
+ /// <summary>
+ /// Interface IHttpResultFactory
+ /// </summary>
+ public interface IHttpResultFactory
+ {
+ /// <summary>
+ /// Gets the result.
+ /// </summary>
+ /// <param name="content">The content.</param>
+ /// <param name="contentType">Type of the content.</param>
+ /// <param name="responseHeaders">The response headers.</param>
+ /// <returns>System.Object.</returns>
+ object GetResult(string content, string contentType, IDictionary<string, string> responseHeaders = null);
+
+ object GetResult(IRequest requestContext, byte[] content, string contentType, IDictionary<string, string> responseHeaders = null);
+ object GetResult(IRequest requestContext, Stream content, string contentType, IDictionary<string, string> responseHeaders = null);
+ object GetResult(IRequest requestContext, string content, string contentType, IDictionary<string, string> responseHeaders = null);
+
+ object GetRedirectResult(string url);
+
+ object GetResult<T>(IRequest requestContext, T result, IDictionary<string, string> responseHeaders = null)
+ where T : class;
+
+ /// <summary>
+ /// Gets the static result.
+ /// </summary>
+ /// <param name="requestContext">The request context.</param>
+ /// <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>
+ /// <param name="responseHeaders">The response headers.</param>
+ /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
+ /// <returns>System.Object.</returns>
+ Task<object> GetStaticResult(IRequest requestContext,
+ Guid cacheKey,
+ DateTime? lastDateModified,
+ TimeSpan? cacheDuration,
+ string contentType, Func<Task<Stream>> factoryFn,
+ IDictionary<string, string> responseHeaders = null,
+ bool isHeadRequest = false);
+
+ /// <summary>
+ /// Gets the static result.
+ /// </summary>
+ /// <param name="requestContext">The request context.</param>
+ /// <param name="options">The options.</param>
+ /// <returns>System.Object.</returns>
+ Task<object> GetStaticResult(IRequest requestContext, StaticResultOptions options);
+
+ /// <summary>
+ /// Gets the static file result.
+ /// </summary>
+ /// <param name="requestContext">The request context.</param>
+ /// <param name="path">The path.</param>
+ /// <param name="fileShare">The file share.</param>
+ /// <returns>System.Object.</returns>
+ Task<object> GetStaticFileResult(IRequest requestContext, string path, FileShareMode fileShare = FileShareMode.Read);
+
+ /// <summary>
+ /// Gets the static file result.
+ /// </summary>
+ /// <param name="requestContext">The request context.</param>
+ /// <param name="options">The options.</param>
+ /// <returns>System.Object.</returns>
+ Task<object> GetStaticFileResult(IRequest requestContext,
+ StaticFileResultOptions options);
+ }
+}
diff --git a/MediaBrowser.Controller/Net/IHttpServer.cs b/MediaBrowser.Controller/Net/IHttpServer.cs
new file mode 100644
index 000000000..d2ebadcfa
--- /dev/null
+++ b/MediaBrowser.Controller/Net/IHttpServer.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using MediaBrowser.Model.Services;
+using MediaBrowser.Model.Events;
+
+namespace MediaBrowser.Controller.Net
+{
+ /// <summary>
+ /// Interface IHttpServer
+ /// </summary>
+ public interface IHttpServer : IDisposable
+ {
+ /// <summary>
+ /// Gets the URL prefix.
+ /// </summary>
+ /// <value>The URL prefix.</value>
+ string[] UrlPrefixes { get; }
+
+ /// <summary>
+ /// Stops this instance.
+ /// </summary>
+ void Stop();
+
+ /// <summary>
+ /// Occurs when [web socket connected].
+ /// </summary>
+ event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected;
+
+ /// <summary>
+ /// Inits this instance.
+ /// </summary>
+ void Init(IEnumerable<IService> services, IEnumerable<IWebSocketListener> listener);
+
+ /// <summary>
+ /// If set, all requests will respond with this message
+ /// </summary>
+ string GlobalResponse { get; set; }
+ }
+}
diff --git a/MediaBrowser.Controller/Net/ISessionContext.cs b/MediaBrowser.Controller/Net/ISessionContext.cs
new file mode 100644
index 000000000..37ddbc2b3
--- /dev/null
+++ b/MediaBrowser.Controller/Net/ISessionContext.cs
@@ -0,0 +1,16 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Session;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
+
+namespace MediaBrowser.Controller.Net
+{
+ public interface ISessionContext
+ {
+ SessionInfo GetSession(object requestContext);
+ User GetUser(object requestContext);
+
+ SessionInfo GetSession(IRequest requestContext);
+ User GetUser(IRequest requestContext);
+ }
+}
diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs
new file mode 100644
index 000000000..816e9afca
--- /dev/null
+++ b/MediaBrowser.Controller/Net/IWebSocketConnection.cs
@@ -0,0 +1,85 @@
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Services;
+using System.Net.WebSockets;
+using System.Threading.Tasks;
+using System.Threading;
+using System;
+
+namespace MediaBrowser.Controller.Net
+{
+ public interface IWebSocketConnection : IDisposable
+ {
+ /// <summary>
+ /// Occurs when [closed].
+ /// </summary>
+ event EventHandler<EventArgs> Closed;
+
+ /// <summary>
+ /// Gets the id.
+ /// </summary>
+ /// <value>The id.</value>
+ Guid Id { get; }
+
+ /// <summary>
+ /// Gets the last activity date.
+ /// </summary>
+ /// <value>The last activity date.</value>
+ DateTime LastActivityDate { get; }
+
+ /// <summary>
+ /// Gets or sets the URL.
+ /// </summary>
+ /// <value>The URL.</value>
+ string Url { get; set; }
+ /// <summary>
+ /// Gets or sets the query string.
+ /// </summary>
+ /// <value>The query string.</value>
+ QueryParamCollection QueryString { get; set; }
+
+ /// <summary>
+ /// Gets or sets the receive action.
+ /// </summary>
+ /// <value>The receive action.</value>
+ Func<WebSocketMessageInfo, Task> OnReceive { get; set; }
+
+ /// <summary>
+ /// Gets the state.
+ /// </summary>
+ /// <value>The state.</value>
+ WebSocketState State { get; }
+
+ /// <summary>
+ /// Gets the remote end point.
+ /// </summary>
+ /// <value>The remote end point.</value>
+ string RemoteEndPoint { get; }
+
+ /// <summary>
+ /// Sends a message asynchronously.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="message">The message.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ /// <exception cref="System.ArgumentNullException">message</exception>
+ Task SendAsync<T>(WebSocketMessage<T> message, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Sends a message asynchronously.
+ /// </summary>
+ /// <param name="buffer">The buffer.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task SendAsync(byte[] buffer, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Sends a message asynchronously.
+ /// </summary>
+ /// <param name="text">The text.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ /// <exception cref="System.ArgumentNullException">buffer</exception>
+ Task SendAsync(string text, CancellationToken cancellationToken);
+ }
+}
diff --git a/MediaBrowser.Controller/Net/IWebSocketListener.cs b/MediaBrowser.Controller/Net/IWebSocketListener.cs
new file mode 100644
index 000000000..29698c1a4
--- /dev/null
+++ b/MediaBrowser.Controller/Net/IWebSocketListener.cs
@@ -0,0 +1,17 @@
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Net
+{
+ /// <summary>
+ ///This is an interface for listening to messages coming through a web socket connection
+ /// </summary>
+ public interface IWebSocketListener
+ {
+ /// <summary>
+ /// Processes the message.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <returns>Task.</returns>
+ Task ProcessMessage(WebSocketMessageInfo message);
+ }
+}
diff --git a/MediaBrowser.Controller/Net/SecurityException.cs b/MediaBrowser.Controller/Net/SecurityException.cs
new file mode 100644
index 000000000..b251ab9a9
--- /dev/null
+++ b/MediaBrowser.Controller/Net/SecurityException.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace MediaBrowser.Controller.Net
+{
+ public class SecurityException : Exception
+ {
+ public SecurityException(string message)
+ : base(message)
+ {
+
+ }
+
+ public SecurityExceptionType SecurityExceptionType { get; set; }
+ }
+
+ public enum SecurityExceptionType
+ {
+ Unauthenticated = 0,
+ ParentalControl = 1
+ }
+}
diff --git a/MediaBrowser.Controller/Net/StaticResultOptions.cs b/MediaBrowser.Controller/Net/StaticResultOptions.cs
new file mode 100644
index 000000000..1c9b2586d
--- /dev/null
+++ b/MediaBrowser.Controller/Net/StaticResultOptions.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+
+using MediaBrowser.Model.IO;
+
+namespace MediaBrowser.Controller.Net
+{
+ public class StaticResultOptions
+ {
+ public string ContentType { get; set; }
+ public TimeSpan? CacheDuration { get; set; }
+ public DateTime? DateLastModified { get; set; }
+ public Guid CacheKey { get; set; }
+
+ public Func<Task<Stream>> ContentFactory { get; set; }
+
+ public bool IsHeadRequest { get; set; }
+
+ public IDictionary<string, string> ResponseHeaders { get; set; }
+
+ public Action OnComplete { get; set; }
+ public Action OnError { get; set; }
+
+ public string Path { get; set; }
+ public long? ContentLength { get; set; }
+
+ public FileShareMode FileShare { get; set; }
+
+ public StaticResultOptions()
+ {
+ ResponseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ FileShare = FileShareMode.Read;
+ }
+ }
+
+ public class StaticFileResultOptions : StaticResultOptions
+ {
+ }
+}
diff --git a/MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs b/MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs
new file mode 100644
index 000000000..b200f883a
--- /dev/null
+++ b/MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Specialized;
+using MediaBrowser.Model.Services;
+
+namespace MediaBrowser.Controller.Net
+{
+ /// <summary>
+ /// Class WebSocketConnectEventArgs
+ /// </summary>
+
+ public class WebSocketConnectingEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Gets or sets the URL.
+ /// </summary>
+ /// <value>The URL.</value>
+ public string Url { get; set; }
+ /// <summary>
+ /// Gets or sets the endpoint.
+ /// </summary>
+ /// <value>The endpoint.</value>
+ public string Endpoint { get; set; }
+ /// <summary>
+ /// Gets or sets the query string.
+ /// </summary>
+ /// <value>The query string.</value>
+ public QueryParamCollection QueryString { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether [allow connection].
+ /// </summary>
+ /// <value><c>true</c> if [allow connection]; otherwise, <c>false</c>.</value>
+ public bool AllowConnection { get; set; }
+
+ public WebSocketConnectingEventArgs()
+ {
+ QueryString = new QueryParamCollection();
+ AllowConnection = true;
+ }
+ }
+
+}
diff --git a/MediaBrowser.Controller/Net/WebSocketMessageInfo.cs b/MediaBrowser.Controller/Net/WebSocketMessageInfo.cs
new file mode 100644
index 000000000..332f16420
--- /dev/null
+++ b/MediaBrowser.Controller/Net/WebSocketMessageInfo.cs
@@ -0,0 +1,16 @@
+using MediaBrowser.Model.Net;
+
+namespace MediaBrowser.Controller.Net
+{
+ /// <summary>
+ /// Class WebSocketMessageInfo
+ /// </summary>
+ public class WebSocketMessageInfo : WebSocketMessage<string>
+ {
+ /// <summary>
+ /// Gets or sets the connection.
+ /// </summary>
+ /// <value>The connection.</value>
+ public IWebSocketConnection Connection { get; set; }
+ }
+} \ No newline at end of file