296 lines
9.0 KiB
C#
296 lines
9.0 KiB
C#
// **********************************************************************************
|
|
// CassiniDev - http://cassinidev.codeplex.com
|
|
//
|
|
// Copyright (c) 2010 Sky Sanders. All rights reserved.
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// This source code is subject to terms and conditions of the Microsoft Public
|
|
// License (Ms-PL). A copy of the license can be found in the license.txt file
|
|
// included in this distribution.
|
|
//
|
|
// You must not remove this notice, or any other, from this software.
|
|
//
|
|
// **********************************************************************************
|
|
|
|
#region
|
|
|
|
using System;
|
|
using System.Globalization;
|
|
using System.Security.Permissions;
|
|
using System.Security.Principal;
|
|
using System.Threading;
|
|
using System.Web;
|
|
using System.Web.Hosting;
|
|
|
|
#endregion
|
|
|
|
namespace CassiniDev
|
|
{
|
|
/// <summary>
|
|
/// 01/01/10 sky: added HttpRuntime.Close to IRegisteredObject.Stop to eliminate
|
|
/// System.AppDomainUnloadedException when running tests in NUnit GuiRunner.
|
|
/// reference: http://stackoverflow.com/questions/561402/cassini-webserver-webdev-nunit-and-appdomainunloadedexception
|
|
/// need to test thoroughly but seems to work just fine with no ill effects
|
|
/// 01.03.10 sky: removed the HttpRuntime.Close because, even though it tests fine, I am not entirely certain it is in the right place
|
|
/// and since I am no longer recommending that the server be used as a library in testing (run a console instance in a new process).
|
|
///
|
|
/// </summary>
|
|
internal class Host : MarshalByRefObject, IRegisteredObject
|
|
{
|
|
private bool _disableDirectoryListing;
|
|
|
|
private string _installPath;
|
|
|
|
private string _lowerCasedClientScriptPathWithTrailingSlash;
|
|
|
|
private string _lowerCasedVirtualPath;
|
|
|
|
private string _lowerCasedVirtualPathWithTrailingSlash;
|
|
|
|
private volatile int _pendingCallsCount;
|
|
|
|
private string _physicalClientScriptPath;
|
|
|
|
private string _physicalPath;
|
|
|
|
private int _port;
|
|
|
|
private bool _requireAuthentication;
|
|
|
|
private Server _server;
|
|
|
|
private string _virtualPath;
|
|
|
|
public Host()
|
|
{
|
|
HostingEnvironment.RegisterObject(this);
|
|
}
|
|
|
|
public bool DisableDirectoryListing
|
|
{
|
|
get { return _disableDirectoryListing; }
|
|
}
|
|
|
|
public string InstallPath
|
|
{
|
|
get { return _installPath; }
|
|
}
|
|
|
|
public string NormalizedClientScriptPath
|
|
{
|
|
get { return _lowerCasedClientScriptPathWithTrailingSlash; }
|
|
}
|
|
|
|
public string NormalizedVirtualPath
|
|
{
|
|
get { return _lowerCasedVirtualPathWithTrailingSlash; }
|
|
}
|
|
|
|
public string PhysicalClientScriptPath
|
|
{
|
|
get { return _physicalClientScriptPath; }
|
|
}
|
|
|
|
public string PhysicalPath
|
|
{
|
|
get { return _physicalPath; }
|
|
}
|
|
|
|
public int Port
|
|
{
|
|
get { return _port; }
|
|
}
|
|
|
|
public bool RequireAuthentication
|
|
{
|
|
get { return _requireAuthentication; }
|
|
}
|
|
|
|
public string VirtualPath
|
|
{
|
|
get { return _virtualPath; }
|
|
}
|
|
|
|
#region IRegisteredObject Members
|
|
|
|
void IRegisteredObject.Stop(bool immediate)
|
|
{
|
|
// Unhook the Host so Server will process the requests in the new appdomain.
|
|
|
|
if (_server != null)
|
|
{
|
|
_server.HostStopped();
|
|
}
|
|
|
|
// Make sure all the pending calls complete before this Object is unregistered.
|
|
WaitForPendingCallsToFinish();
|
|
|
|
HostingEnvironment.UnregisterObject(this);
|
|
}
|
|
|
|
#endregion
|
|
|
|
public void Configure(Server server, int port, string virtualPath, string physicalPath,
|
|
bool requireAuthentication)
|
|
{
|
|
Configure(server, port, virtualPath, physicalPath, requireAuthentication, false);
|
|
}
|
|
|
|
public void Configure(Server server, int port, string virtualPath, string physicalPath)
|
|
{
|
|
Configure(server, port, virtualPath, physicalPath, false, false);
|
|
}
|
|
|
|
public void Configure(Server server, int port, string virtualPath, string physicalPath,
|
|
bool requireAuthentication, bool disableDirectoryListing)
|
|
{
|
|
_server = server;
|
|
|
|
_port = port;
|
|
_installPath = null;
|
|
_virtualPath = virtualPath;
|
|
_requireAuthentication = requireAuthentication;
|
|
_disableDirectoryListing = disableDirectoryListing;
|
|
_lowerCasedVirtualPath = CultureInfo.InvariantCulture.TextInfo.ToLower(_virtualPath);
|
|
_lowerCasedVirtualPathWithTrailingSlash = virtualPath.EndsWith("/", StringComparison.Ordinal)
|
|
? virtualPath
|
|
: virtualPath + "/";
|
|
_lowerCasedVirtualPathWithTrailingSlash =
|
|
CultureInfo.InvariantCulture.TextInfo.ToLower(_lowerCasedVirtualPathWithTrailingSlash);
|
|
_physicalPath = physicalPath;
|
|
_physicalClientScriptPath = HttpRuntime.AspClientScriptPhysicalPath + "\\";
|
|
_lowerCasedClientScriptPathWithTrailingSlash =
|
|
CultureInfo.InvariantCulture.TextInfo.ToLower(HttpRuntime.AspClientScriptVirtualPath + "/");
|
|
}
|
|
|
|
public SecurityIdentifier GetProcessSid()
|
|
{
|
|
using (WindowsIdentity identity = new WindowsIdentity(_server.GetProcessToken()))
|
|
{
|
|
return identity.User;
|
|
}
|
|
}
|
|
|
|
public IntPtr GetProcessToken()
|
|
{
|
|
new SecurityPermission(PermissionState.Unrestricted).Assert();
|
|
return _server.GetProcessToken();
|
|
}
|
|
|
|
public string GetProcessUser()
|
|
{
|
|
return _server.GetProcessUser();
|
|
}
|
|
|
|
public override object InitializeLifetimeService()
|
|
{
|
|
// never expire the license
|
|
return null;
|
|
}
|
|
|
|
public bool IsVirtualPathAppPath(string path)
|
|
{
|
|
if (path == null)
|
|
{
|
|
return false;
|
|
}
|
|
path = CultureInfo.InvariantCulture.TextInfo.ToLower(path);
|
|
return (path == _lowerCasedVirtualPath || path == _lowerCasedVirtualPathWithTrailingSlash);
|
|
}
|
|
|
|
public bool IsVirtualPathInApp(string path, out bool isClientScriptPath)
|
|
{
|
|
isClientScriptPath = false;
|
|
|
|
if (path == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (_virtualPath == "/" && path.StartsWith("/", StringComparison.Ordinal))
|
|
{
|
|
if (path.StartsWith(_lowerCasedClientScriptPathWithTrailingSlash, StringComparison.Ordinal))
|
|
{
|
|
isClientScriptPath = true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
path = CultureInfo.InvariantCulture.TextInfo.ToLower(path);
|
|
|
|
if (path.StartsWith(_lowerCasedVirtualPathWithTrailingSlash, StringComparison.Ordinal))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (path == _lowerCasedVirtualPath)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (path.StartsWith(_lowerCasedClientScriptPathWithTrailingSlash, StringComparison.Ordinal))
|
|
{
|
|
isClientScriptPath = true;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public bool IsVirtualPathInApp(String path)
|
|
{
|
|
bool isClientScriptPath;
|
|
return IsVirtualPathInApp(path, out isClientScriptPath);
|
|
}
|
|
|
|
public void ProcessRequest(Connection conn)
|
|
{
|
|
// Add a pending call to make sure our thread doesn't get killed
|
|
AddPendingCall();
|
|
|
|
try
|
|
{
|
|
new Request(_server, this, conn).Process();
|
|
}
|
|
finally
|
|
{
|
|
RemovePendingCall();
|
|
}
|
|
}
|
|
|
|
[SecurityPermission(SecurityAction.Assert, Unrestricted = true)]
|
|
public void Shutdown()
|
|
{
|
|
HostingEnvironment.InitiateShutdown();
|
|
}
|
|
|
|
private void AddPendingCall()
|
|
{
|
|
//TODO: investigate this issue - ref var not volitile
|
|
#pragma warning disable 0420
|
|
Interlocked.Increment(ref _pendingCallsCount);
|
|
#pragma warning restore 0420
|
|
}
|
|
|
|
private void RemovePendingCall()
|
|
{
|
|
//TODO: investigate this issue - ref var not volitile
|
|
#pragma warning disable 0420
|
|
Interlocked.Decrement(ref _pendingCallsCount);
|
|
#pragma warning restore 0420
|
|
}
|
|
|
|
private void WaitForPendingCallsToFinish()
|
|
{
|
|
for (;;)
|
|
{
|
|
if (_pendingCallsCount <= 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Thread.Sleep(250);
|
|
}
|
|
}
|
|
}
|
|
} |