2014-07-26 10:30:15 -07:00
using Funq ;
2013-12-07 08:52:38 -07:00
using MediaBrowser.Common ;
using MediaBrowser.Common.Extensions ;
2015-06-12 21:14:48 -07:00
using MediaBrowser.Controller.Configuration ;
2013-12-07 08:52:38 -07:00
using MediaBrowser.Controller.Net ;
using MediaBrowser.Model.Logging ;
2014-07-18 18:28:40 -07:00
using MediaBrowser.Server.Implementations.HttpServer.SocketSharp ;
2013-12-07 08:52:38 -07:00
using ServiceStack ;
2013-12-10 09:44:07 -07:00
using ServiceStack.Api.Swagger ;
2013-12-07 08:52:38 -07:00
using ServiceStack.Host ;
using ServiceStack.Host.Handlers ;
using ServiceStack.Host.HttpListener ;
using ServiceStack.Logging ;
using ServiceStack.Web ;
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
using System.Reflection ;
using System.Threading ;
using System.Threading.Tasks ;
2015-12-14 07:45:42 -07:00
using MediaBrowser.Common.Net ;
2015-10-30 10:00:33 -07:00
using MediaBrowser.Common.Security ;
2016-03-17 20:40:15 -07:00
using MediaBrowser.Model.Extensions ;
2013-12-07 08:52:38 -07:00
namespace MediaBrowser.Server.Implementations.HttpServer
{
public class HttpListenerHost : ServiceStackHost , IHttpServer
{
private string DefaultRedirectPath { get ; set ; }
private readonly ILogger _logger ;
2014-01-08 21:44:51 -07:00
public IEnumerable < string > UrlPrefixes { get ; private set ; }
2013-12-07 08:52:38 -07:00
private readonly List < IRestfulService > _restServices = new List < IRestfulService > ( ) ;
2014-07-18 15:14:59 -07:00
private IHttpListener _listener ;
2013-12-07 08:52:38 -07:00
private readonly ContainerAdapter _containerAdapter ;
public event EventHandler < WebSocketConnectEventArgs > WebSocketConnected ;
2015-03-08 12:48:30 -07:00
public event EventHandler < WebSocketConnectingEventArgs > WebSocketConnecting ;
2013-12-07 08:52:38 -07:00
2015-01-18 21:29:57 -07:00
public string CertificatePath { get ; private set ; }
2015-01-06 20:36:42 -07:00
2015-06-12 21:14:48 -07:00
private readonly IServerConfigurationManager _config ;
2015-12-14 07:45:42 -07:00
private readonly INetworkManager _networkManager ;
2015-06-12 21:14:48 -07:00
2015-01-17 12:30:23 -07:00
public HttpListenerHost ( IApplicationHost applicationHost ,
2015-10-30 10:00:33 -07:00
ILogManager logManager ,
2015-06-12 21:14:48 -07:00
IServerConfigurationManager config ,
2015-01-17 12:30:23 -07:00
string serviceName ,
2015-12-14 07:45:42 -07:00
string defaultRedirectPath , INetworkManager networkManager , params Assembly [ ] assembliesWithServices )
2013-12-07 08:52:38 -07:00
: base ( serviceName , assembliesWithServices )
{
DefaultRedirectPath = defaultRedirectPath ;
2015-12-14 07:45:42 -07:00
_networkManager = networkManager ;
2015-06-12 21:14:48 -07:00
_config = config ;
2013-12-07 08:52:38 -07:00
_logger = logManager . GetLogger ( "HttpServer" ) ;
_containerAdapter = new ContainerAdapter ( applicationHost ) ;
}
2015-09-13 16:07:54 -07:00
public string GlobalResponse { get ; set ; }
2015-10-30 10:00:33 -07:00
2013-12-07 08:52:38 -07:00
public override void Configure ( Container container )
{
HostConfig . Instance . DefaultRedirectPath = DefaultRedirectPath ;
HostConfig . Instance . MapExceptionToStatusCode = new Dictionary < Type , int >
{
{ typeof ( InvalidOperationException ) , 422 } ,
{ typeof ( ResourceNotFoundException ) , 404 } ,
{ typeof ( FileNotFoundException ) , 404 } ,
2014-07-21 18:29:06 -07:00
{ typeof ( DirectoryNotFoundException ) , 404 } ,
2014-11-14 19:31:03 -07:00
{ typeof ( SecurityException ) , 401 } ,
2015-10-30 10:00:33 -07:00
{ typeof ( PaymentRequiredException ) , 402 } ,
{ typeof ( UnauthorizedAccessException ) , 500 } ,
{ typeof ( ApplicationException ) , 500 }
2013-12-07 08:52:38 -07:00
} ;
HostConfig . Instance . DebugMode = true ;
HostConfig . Instance . LogFactory = LogManager . LogFactory ;
// The Markdown feature causes slow startup times (5 mins+) on cold boots for some users
// Custom format allows images
HostConfig . Instance . EnableFeatures = Feature . Csv | Feature . Html | Feature . Json | Feature . Jsv | Feature . Metadata | Feature . Xml | Feature . CustomFormat ;
container . Adapter = _containerAdapter ;
2013-12-10 09:44:07 -07:00
Plugins . Add ( new SwaggerFeature ( ) ) ;
2015-07-09 20:00:03 -07:00
Plugins . Add ( new CorsFeature ( allowedHeaders : "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization" ) ) ;
2014-07-01 21:57:18 -07:00
2014-07-08 17:46:11 -07:00
//Plugins.Add(new AuthFeature(() => new AuthUserSession(), new IAuthProvider[] {
// new SessionAuthProvider(_containerAdapter.Resolve<ISessionContext>()),
//}));
2014-07-01 21:57:18 -07:00
2014-08-17 20:00:37 -07:00
PreRequestFilters . Add ( ( httpReq , httpRes ) = >
{
//Handles Request and closes Responses after emitting global HTTP Headers
if ( string . Equals ( httpReq . Verb , "OPTIONS" , StringComparison . OrdinalIgnoreCase ) )
{
httpRes . EndRequest ( ) ; //add a 'using ServiceStack;'
}
} ) ;
2015-06-12 21:14:48 -07:00
HostContext . GlobalResponseFilters . Add ( new ResponseFilter ( _logger , ( ) = > _config . Configuration . DenyIFrameEmbedding ) . FilterResponse ) ;
2013-12-07 08:52:38 -07:00
}
public override void OnAfterInit ( )
{
SetAppDomainData ( ) ;
base . OnAfterInit ( ) ;
}
public override void OnConfigLoad ( )
{
base . OnConfigLoad ( ) ;
2015-01-17 12:30:23 -07:00
Config . HandlerFactoryPath = null ;
2013-12-07 08:52:38 -07:00
2015-01-17 12:30:23 -07:00
Config . MetadataRedirectPath = "metadata" ;
2013-12-07 08:52:38 -07:00
}
protected override ServiceController CreateServiceController ( params Assembly [ ] assembliesWithServices )
{
var types = _restServices . Select ( r = > r . GetType ( ) ) . ToArray ( ) ;
return new ServiceController ( this , ( ) = > types ) ;
}
public virtual void SetAppDomainData ( )
{
//Required for Mono to resolve VirtualPathUtility and Url.Content urls
var domain = Thread . GetDomain ( ) ; // or AppDomain.Current
domain . SetData ( ".appDomain" , "1" ) ;
domain . SetData ( ".appVPath" , "/" ) ;
domain . SetData ( ".appPath" , domain . BaseDirectory ) ;
if ( string . IsNullOrEmpty ( domain . GetData ( ".appId" ) as string ) )
{
domain . SetData ( ".appId" , "1" ) ;
}
if ( string . IsNullOrEmpty ( domain . GetData ( ".domainId" ) as string ) )
{
domain . SetData ( ".domainId" , "1" ) ;
}
}
public override ServiceStackHost Start ( string listeningAtUrlBase )
{
2014-07-18 15:14:59 -07:00
StartListener ( ) ;
2013-12-07 08:52:38 -07:00
return this ;
}
/// <summary>
/// Starts the Web Service
/// </summary>
2014-07-18 15:14:59 -07:00
private void StartListener ( )
2013-12-07 08:52:38 -07:00
{
2014-07-08 17:46:11 -07:00
HostContext . Config . HandlerFactoryPath = ListenerRequest . GetHandlerPathIfAny ( UrlPrefixes . First ( ) ) ;
2014-12-27 15:52:41 -07:00
_listener = GetListener ( ) ;
2014-07-18 18:28:40 -07:00
2015-03-08 12:48:30 -07:00
_listener . WebSocketConnected = OnWebSocketConnected ;
_listener . WebSocketConnecting = OnWebSocketConnecting ;
2014-07-18 18:28:40 -07:00
_listener . ErrorHandler = ErrorHandler ;
_listener . RequestHandler = RequestHandler ;
2013-12-07 08:52:38 -07:00
2014-07-18 15:14:59 -07:00
_listener . Start ( UrlPrefixes ) ;
2014-07-08 17:46:11 -07:00
}
2013-12-07 08:52:38 -07:00
2014-12-27 15:52:41 -07:00
private IHttpListener GetListener ( )
{
2016-01-21 12:45:52 -07:00
return new WebSocketSharpListener ( _logger , CertificatePath ) ;
2014-12-27 15:52:41 -07:00
}
2015-03-08 12:48:30 -07:00
private void OnWebSocketConnecting ( WebSocketConnectingEventArgs args )
{
2016-04-22 09:12:20 -07:00
if ( _disposed )
{
return ;
}
2015-03-08 12:48:30 -07:00
if ( WebSocketConnecting ! = null )
{
WebSocketConnecting ( this , args ) ;
}
}
private void OnWebSocketConnected ( WebSocketConnectEventArgs args )
2014-07-08 17:46:11 -07:00
{
2016-04-22 09:12:20 -07:00
if ( _disposed )
{
return ;
}
2014-07-18 15:14:59 -07:00
if ( WebSocketConnected ! = null )
2014-07-08 17:46:11 -07:00
{
2014-07-18 15:14:59 -07:00
WebSocketConnected ( this , args ) ;
2014-07-08 17:46:11 -07:00
}
2013-12-07 08:52:38 -07:00
}
2014-07-18 15:14:59 -07:00
private void ErrorHandler ( Exception ex , IRequest httpReq )
2013-12-07 08:52:38 -07:00
{
try
{
var httpRes = httpReq . Response ;
2014-07-03 19:22:57 -07:00
if ( httpRes . IsClosed )
{
return ;
}
2015-01-17 12:30:23 -07:00
2014-07-08 17:46:11 -07:00
var errorResponse = new ErrorResponse
{
ResponseStatus = new ResponseStatus
{
ErrorCode = ex . GetType ( ) . GetOperationName ( ) ,
Message = ex . Message ,
2014-10-28 16:17:55 -07:00
StackTrace = ex . StackTrace
2014-07-08 17:46:11 -07:00
}
} ;
2013-12-07 08:52:38 -07:00
var contentType = httpReq . ResponseContentType ;
var serializer = HostContext . ContentTypes . GetResponseSerializer ( contentType ) ;
if ( serializer = = null )
{
contentType = HostContext . Config . DefaultContentType ;
serializer = HostContext . ContentTypes . GetResponseSerializer ( contentType ) ;
}
var httpError = ex as IHttpError ;
if ( httpError ! = null )
{
httpRes . StatusCode = httpError . Status ;
httpRes . StatusDescription = httpError . StatusDescription ;
}
else
{
httpRes . StatusCode = 500 ;
}
httpRes . ContentType = contentType ;
serializer ( httpReq , errorResponse , httpRes ) ;
httpRes . Close ( ) ;
}
catch ( Exception errorEx )
{
2016-02-04 11:04:04 -07:00
//_logger.ErrorException("Error this.ProcessRequest(context)(Exception while writing error to the response)", errorEx);
2013-12-07 08:52:38 -07:00
}
}
/// <summary>
/// Shut down the Web Service
/// </summary>
public void Stop ( )
{
2014-07-18 15:14:59 -07:00
if ( _listener ! = null )
2013-12-07 08:52:38 -07:00
{
2014-07-18 15:14:59 -07:00
_listener . Stop ( ) ;
2013-12-07 08:52:38 -07:00
}
}
2016-01-22 20:10:21 -07:00
private readonly Dictionary < string , int > _skipLogExtensions = new Dictionary < string , int > ( StringComparer . OrdinalIgnoreCase )
{
{ ".js" , 0 } ,
{ ".css" , 0 } ,
{ ".woff" , 0 } ,
{ ".woff2" , 0 } ,
{ ".ttf" , 0 } ,
{ ".html" , 0 }
} ;
2016-02-03 14:56:00 -07:00
private bool EnableLogging ( string url , string localPath )
2016-01-22 20:10:21 -07:00
{
2016-02-01 12:54:49 -07:00
var extension = GetExtension ( url ) ;
2016-01-22 20:10:21 -07:00
2016-02-03 14:56:00 -07:00
if ( string . IsNullOrWhiteSpace ( extension ) | | ! _skipLogExtensions . ContainsKey ( extension ) )
{
if ( string . IsNullOrWhiteSpace ( localPath ) | | localPath . IndexOf ( "system/ping" , StringComparison . OrdinalIgnoreCase ) = = - 1 )
{
return true ;
}
}
return false ;
2016-01-22 20:10:21 -07:00
}
2016-02-01 12:54:49 -07:00
private string GetExtension ( string url )
{
var parts = url . Split ( new [ ] { '?' } , 2 ) ;
return Path . GetExtension ( parts [ 0 ] ) ;
}
2016-03-15 12:11:53 -07:00
public static string RemoveQueryStringByKey ( string url , string key )
{
var uri = new Uri ( url ) ;
// this gets all the query string key value pairs as a collection
var newQueryString = MyHttpUtility . ParseQueryString ( uri . Query ) ;
if ( newQueryString . Count = = 0 )
{
return url ;
}
// this removes the key if exists
newQueryString . Remove ( key ) ;
// this gets the page path from root without QueryString
string pagePathWithoutQueryString = uri . GetLeftPart ( UriPartial . Path ) ;
return newQueryString . Count > 0
? String . Format ( "{0}?{1}" , pagePathWithoutQueryString , newQueryString )
: pagePathWithoutQueryString ;
}
private string GetUrlToLog ( string url )
{
url = RemoveQueryStringByKey ( url , "api_key" ) ;
return url ;
}
2013-12-07 08:52:38 -07:00
/// <summary>
/// Overridable method that can be used to implement a custom hnandler
/// </summary>
2014-07-18 15:14:59 -07:00
/// <param name="httpReq">The HTTP req.</param>
2014-07-18 18:28:40 -07:00
/// <param name="url">The URL.</param>
2014-07-18 15:14:59 -07:00
/// <returns>Task.</returns>
protected Task RequestHandler ( IHttpRequest httpReq , Uri url )
2013-12-07 08:52:38 -07:00
{
2014-07-18 15:14:59 -07:00
var date = DateTime . Now ;
2014-07-08 17:46:11 -07:00
2014-07-18 15:14:59 -07:00
var httpRes = httpReq . Response ;
2014-07-08 17:46:11 -07:00
2016-04-22 09:12:20 -07:00
if ( _disposed )
{
httpRes . StatusCode = 503 ;
httpRes . Close ( ) ;
return Task . FromResult ( true ) ;
}
2014-07-18 15:14:59 -07:00
var operationName = httpReq . OperationName ;
var localPath = url . LocalPath ;
2014-07-08 17:46:11 -07:00
2016-01-27 11:35:37 -07:00
var urlString = url . OriginalString ;
2016-02-03 14:56:00 -07:00
var enableLog = EnableLogging ( urlString , localPath ) ;
2016-03-15 12:11:53 -07:00
var urlToLog = urlString ;
2016-01-22 20:10:21 -07:00
if ( enableLog )
{
2016-03-15 12:11:53 -07:00
urlToLog = GetUrlToLog ( urlString ) ;
LoggerUtils . LogRequest ( _logger , urlToLog , httpReq . HttpMethod , httpReq . UserAgent ) ;
2016-01-22 20:10:21 -07:00
}
2016-03-17 20:40:15 -07:00
2016-04-05 19:18:56 -07:00
if ( string . Equals ( localPath , "/emby/" , StringComparison . OrdinalIgnoreCase ) | |
string . Equals ( localPath , "/mediabrowser/" , StringComparison . OrdinalIgnoreCase ) )
{
httpRes . RedirectToUrl ( DefaultRedirectPath ) ;
return Task . FromResult ( true ) ;
}
if ( string . Equals ( localPath , "/emby" , StringComparison . OrdinalIgnoreCase ) | |
string . Equals ( localPath , "/mediabrowser" , StringComparison . OrdinalIgnoreCase ) )
{
httpRes . RedirectToUrl ( "emby/" + DefaultRedirectPath ) ;
return Task . FromResult ( true ) ;
}
2015-04-11 14:34:05 -07:00
if ( string . Equals ( localPath , "/mediabrowser/" , StringComparison . OrdinalIgnoreCase ) | |
2016-03-17 20:40:15 -07:00
string . Equals ( localPath , "/mediabrowser" , StringComparison . OrdinalIgnoreCase ) | |
localPath . IndexOf ( "mediabrowser/web" , StringComparison . OrdinalIgnoreCase ) ! = - 1 | |
localPath . IndexOf ( "dashboard/" , StringComparison . OrdinalIgnoreCase ) ! = - 1 )
2014-07-08 17:46:11 -07:00
{
2016-03-17 20:40:15 -07:00
httpRes . StatusCode = 200 ;
2016-03-17 23:36:58 -07:00
httpRes . ContentType = "text/html" ;
2016-03-17 20:40:15 -07:00
var newUrl = urlString . Replace ( "mediabrowser" , "emby" , StringComparison . OrdinalIgnoreCase )
. Replace ( "/dashboard/" , "/web/" , StringComparison . OrdinalIgnoreCase ) ;
2016-03-25 10:48:18 -07:00
if ( ! string . Equals ( newUrl , urlString , StringComparison . OrdinalIgnoreCase ) )
{
httpRes . Write ( "<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" + newUrl + "\">" + newUrl + "</a></body></html>" ) ;
2016-03-17 20:40:15 -07:00
2016-03-25 10:48:18 -07:00
httpRes . Close ( ) ;
return Task . FromResult ( true ) ;
}
2014-07-08 17:46:11 -07:00
}
2016-03-17 23:36:58 -07:00
2015-11-14 12:09:49 -07:00
if ( string . Equals ( localPath , "/web" , StringComparison . OrdinalIgnoreCase ) )
{
httpRes . RedirectToUrl ( DefaultRedirectPath ) ;
return Task . FromResult ( true ) ;
}
if ( string . Equals ( localPath , "/web/" , StringComparison . OrdinalIgnoreCase ) )
{
httpRes . RedirectToUrl ( "../" + DefaultRedirectPath ) ;
return Task . FromResult ( true ) ;
}
2014-07-08 17:46:11 -07:00
if ( string . Equals ( localPath , "/" , StringComparison . OrdinalIgnoreCase ) )
{
2015-01-17 12:30:23 -07:00
httpRes . RedirectToUrl ( DefaultRedirectPath ) ;
2014-07-08 17:46:11 -07:00
return Task . FromResult ( true ) ;
}
if ( string . IsNullOrEmpty ( localPath ) )
{
2015-01-17 12:30:23 -07:00
httpRes . RedirectToUrl ( "/" + DefaultRedirectPath ) ;
2014-07-08 17:46:11 -07:00
return Task . FromResult ( true ) ;
}
2016-02-20 23:25:25 -07:00
if ( string . Equals ( localPath , "/emby/pin" , StringComparison . OrdinalIgnoreCase ) )
{
httpRes . RedirectToUrl ( "web/pin.html" ) ;
return Task . FromResult ( true ) ;
}
2016-03-17 23:36:58 -07:00
2015-09-13 16:07:54 -07:00
if ( ! string . IsNullOrWhiteSpace ( GlobalResponse ) )
{
2016-02-14 21:46:51 -07:00
httpRes . StatusCode = 503 ;
httpRes . ContentType = "text/html" ;
httpRes . Write ( GlobalResponse ) ;
2016-02-01 12:54:49 -07:00
2016-02-14 21:46:51 -07:00
httpRes . Close ( ) ;
2015-09-13 16:07:54 -07:00
return Task . FromResult ( true ) ;
}
2013-12-07 08:52:38 -07:00
var handler = HttpHandlerFactory . GetHandler ( httpReq ) ;
2014-07-08 17:46:11 -07:00
var remoteIp = httpReq . RemoteIp ;
2013-12-07 08:52:38 -07:00
var serviceStackHandler = handler as IServiceStackHandler ;
if ( serviceStackHandler ! = null )
{
var restHandler = serviceStackHandler as RestHandler ;
if ( restHandler ! = null )
{
httpReq . OperationName = operationName = restHandler . RestPath . RequestType . GetOperationName ( ) ;
}
var task = serviceStackHandler . ProcessRequestAsync ( httpReq , httpRes , operationName ) ;
2014-07-08 17:46:11 -07:00
task . ContinueWith ( x = > httpRes . Close ( ) , TaskContinuationOptions . OnlyOnRanToCompletion | TaskContinuationOptions . AttachedToParent ) ;
//Matches Exceptions handled in HttpListenerBase.InitTask()
task . ContinueWith ( x = >
{
2014-07-18 18:28:40 -07:00
var statusCode = httpRes . StatusCode ;
2014-07-08 17:46:11 -07:00
var duration = DateTime . Now - date ;
2016-01-22 20:10:21 -07:00
if ( enableLog )
{
2016-03-15 12:11:53 -07:00
LoggerUtils . LogResponse ( _logger , statusCode , urlToLog , remoteIp , duration ) ;
2016-01-22 20:10:21 -07:00
}
2014-07-08 17:46:11 -07:00
} , TaskContinuationOptions . None ) ;
2013-12-07 08:52:38 -07:00
return task ;
}
return new NotImplementedException ( "Cannot execute handler: " + handler + " at PathInfo: " + httpReq . PathInfo )
. AsTaskException ( ) ;
}
/// <summary>
/// Adds the rest handlers.
/// </summary>
/// <param name="services">The services.</param>
public void Init ( IEnumerable < IRestfulService > services )
{
_restServices . AddRange ( services ) ;
ServiceController = CreateServiceController ( ) ;
_logger . Info ( "Calling ServiceStack AppHost.Init" ) ;
2013-12-08 19:24:48 -07:00
base . Init ( ) ;
2013-12-07 08:52:38 -07:00
}
2015-01-17 12:30:23 -07:00
public override RouteAttribute [ ] GetRouteAttributes ( Type requestType )
{
var routes = base . GetRouteAttributes ( requestType ) . ToList ( ) ;
var clone = routes . ToList ( ) ;
foreach ( var route in clone )
{
2015-04-11 14:34:05 -07:00
routes . Add ( new RouteAttribute ( NormalizeEmbyRoutePath ( route . Path ) , route . Verbs )
{
Notes = route . Notes ,
Priority = route . Priority ,
Summary = route . Summary
} ) ;
2016-03-17 20:40:15 -07:00
2015-01-17 12:30:23 -07:00
routes . Add ( new RouteAttribute ( NormalizeRoutePath ( route . Path ) , route . Verbs )
{
Notes = route . Notes ,
Priority = route . Priority ,
Summary = route . Summary
} ) ;
2015-02-18 21:37:44 -07:00
2015-04-11 14:34:05 -07:00
routes . Add ( new RouteAttribute ( DoubleNormalizeEmbyRoutePath ( route . Path ) , route . Verbs )
{
Notes = route . Notes ,
Priority = route . Priority ,
Summary = route . Summary
} ) ;
2015-01-17 12:30:23 -07:00
}
return routes . ToArray ( ) ;
}
2015-04-11 14:34:05 -07:00
private string NormalizeEmbyRoutePath ( string path )
{
if ( path . StartsWith ( "/" , StringComparison . OrdinalIgnoreCase ) )
{
return "/emby" + path ;
}
return "emby/" + path ;
}
private string DoubleNormalizeEmbyRoutePath ( string path )
{
if ( path . StartsWith ( "/" , StringComparison . OrdinalIgnoreCase ) )
{
return "/emby/emby" + path ;
}
return "emby/emby/" + path ;
}
2015-01-17 12:30:23 -07:00
private string NormalizeRoutePath ( string path )
{
if ( path . StartsWith ( "/" , StringComparison . OrdinalIgnoreCase ) )
{
return "/mediabrowser" + path ;
}
return "mediabrowser/" + path ;
}
2014-07-08 17:46:11 -07:00
2013-12-07 08:52:38 -07:00
/// <summary>
/// Releases the specified instance.
/// </summary>
/// <param name="instance">The instance.</param>
public override void Release ( object instance )
{
// Leave this empty so SS doesn't try to dispose our objects
}
private bool _disposed ;
private readonly object _disposeLock = new object ( ) ;
protected virtual void Dispose ( bool disposing )
{
if ( _disposed ) return ;
base . Dispose ( ) ;
lock ( _disposeLock )
{
if ( _disposed ) return ;
if ( disposing )
{
Stop ( ) ;
}
//release unmanaged resources here...
_disposed = true ;
}
}
public override void Dispose ( )
{
Dispose ( true ) ;
GC . SuppressFinalize ( this ) ;
}
2015-01-06 20:36:42 -07:00
public void StartServer ( IEnumerable < string > urlPrefixes , string certificatePath )
2013-12-07 08:52:38 -07:00
{
2015-01-18 21:29:57 -07:00
CertificatePath = certificatePath ;
2014-01-08 21:44:51 -07:00
UrlPrefixes = urlPrefixes . ToList ( ) ;
Start ( UrlPrefixes . First ( ) ) ;
2013-12-07 08:52:38 -07:00
}
}
}