mirror of
https://github.com/lidarr/lidarr.git
synced 2025-08-14 02:37:08 -07:00
pretty...
This commit is contained in:
parent
beaf0cf939
commit
afafc6b032
285 changed files with 32688 additions and 784 deletions
410
CassiniDev/Core/Connection.cs
Normal file
410
CassiniDev/Core/Connection.cs
Normal file
|
@ -0,0 +1,410 @@
|
|||
// **********************************************************************************
|
||||
// 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.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using CassiniDev.ServerLog;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace CassiniDev
|
||||
{
|
||||
public class Connection : MarshalByRefObject
|
||||
{
|
||||
private const int HttpForbidden = 403;
|
||||
|
||||
private const int HttpOK = 200;
|
||||
|
||||
private readonly MemoryStream _responseContent;
|
||||
|
||||
private readonly Server _server;
|
||||
private LogInfo _requestLog;
|
||||
private LogInfo _responseLog;
|
||||
|
||||
private Socket _socket;
|
||||
|
||||
internal Connection(Server server, Socket socket)
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
_responseContent = new MemoryStream();
|
||||
_server = server;
|
||||
_socket = socket;
|
||||
InitializeLogInfo();
|
||||
}
|
||||
|
||||
public bool Connected
|
||||
{
|
||||
get { return _socket.Connected; }
|
||||
}
|
||||
|
||||
public Guid Id { get; private set; }
|
||||
|
||||
public string LocalIP
|
||||
{
|
||||
get
|
||||
{
|
||||
IPEndPoint ep = (IPEndPoint) _socket.LocalEndPoint;
|
||||
return (ep != null && ep.Address != null) ? ep.Address.ToString() : "127.0.0.1";
|
||||
}
|
||||
}
|
||||
|
||||
public string RemoteIP
|
||||
{
|
||||
get
|
||||
{
|
||||
IPEndPoint ep = (IPEndPoint) _socket.RemoteEndPoint;
|
||||
return (ep != null && ep.Address != null) ? ep.Address.ToString() : "127.0.0.1";
|
||||
}
|
||||
}
|
||||
|
||||
public LogInfo RequestLog
|
||||
{
|
||||
get { return _requestLog; }
|
||||
}
|
||||
|
||||
public LogInfo ResponseLog
|
||||
{
|
||||
get { return _responseLog; }
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
FinalizeLogInfo();
|
||||
|
||||
try
|
||||
{
|
||||
_socket.Shutdown(SocketShutdown.Both);
|
||||
_socket.Close();
|
||||
}
|
||||
// ReSharper disable EmptyGeneralCatchClause
|
||||
catch
|
||||
// ReSharper restore EmptyGeneralCatchClause
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
_socket = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public override object InitializeLifetimeService()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public void LogRequest(string pathTranslated, string url)
|
||||
{
|
||||
_requestLog.PathTranslated = pathTranslated;
|
||||
|
||||
_requestLog.Url = url;
|
||||
}
|
||||
|
||||
public void LogRequestBody(byte[] content)
|
||||
{
|
||||
_requestLog.Body = content;
|
||||
}
|
||||
|
||||
public void LogRequestHeaders(string headers)
|
||||
{
|
||||
_requestLog.Headers = headers;
|
||||
}
|
||||
|
||||
public byte[] ReadRequestBytes(int maxBytes)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (WaitForRequestBytes() == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int numBytes = _socket.Available;
|
||||
|
||||
if (numBytes > maxBytes)
|
||||
{
|
||||
numBytes = maxBytes;
|
||||
}
|
||||
|
||||
int numReceived = 0;
|
||||
|
||||
byte[] buffer = new byte[numBytes];
|
||||
|
||||
if (numBytes > 0)
|
||||
{
|
||||
numReceived = _socket.Receive(buffer, 0, numBytes, SocketFlags.None);
|
||||
}
|
||||
|
||||
if (numReceived < numBytes)
|
||||
{
|
||||
byte[] tempBuffer = new byte[numReceived];
|
||||
|
||||
if (numReceived > 0)
|
||||
{
|
||||
Buffer.BlockCopy(buffer, 0, tempBuffer, 0, numReceived);
|
||||
}
|
||||
|
||||
buffer = tempBuffer;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public int WaitForRequestBytes()
|
||||
{
|
||||
int availBytes = 0;
|
||||
|
||||
try
|
||||
{
|
||||
if (_socket.Available == 0)
|
||||
{
|
||||
_socket.Poll(100000, SelectMode.SelectRead);
|
||||
|
||||
if (_socket.Available == 0 && _socket.Connected)
|
||||
{
|
||||
_socket.Poll(30000000, SelectMode.SelectRead);
|
||||
}
|
||||
}
|
||||
|
||||
availBytes = _socket.Available;
|
||||
}
|
||||
// ReSharper disable EmptyGeneralCatchClause
|
||||
catch
|
||||
// ReSharper restore EmptyGeneralCatchClause
|
||||
{
|
||||
}
|
||||
|
||||
return availBytes;
|
||||
}
|
||||
|
||||
public void Write100Continue()
|
||||
{
|
||||
WriteEntireResponseFromString(100, null, null, true);
|
||||
}
|
||||
|
||||
public void WriteBody(byte[] data, int offset, int length)
|
||||
{
|
||||
try
|
||||
{
|
||||
_responseContent.Write(data, 0, data.Length);
|
||||
_socket.Send(data, offset, length, SocketFlags.None);
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteEntireResponseFromFile(String fileName, bool keepAlive)
|
||||
{
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
WriteErrorAndClose(404);
|
||||
return;
|
||||
}
|
||||
|
||||
// Deny the request if the contentType cannot be recognized.
|
||||
|
||||
string contentType = Common.GetContentType(fileName);
|
||||
|
||||
//TODO: i am pretty sure this is unnecessary
|
||||
if (contentType == null)
|
||||
{
|
||||
WriteErrorAndClose(HttpForbidden);
|
||||
return;
|
||||
}
|
||||
|
||||
string contentTypeHeader = "Content-Type: " + contentType + "\r\n";
|
||||
|
||||
bool completed = false;
|
||||
FileStream fs = null;
|
||||
|
||||
try
|
||||
{
|
||||
fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
int len = (int) fs.Length;
|
||||
byte[] fileBytes = new byte[len];
|
||||
int bytesRead = fs.Read(fileBytes, 0, len);
|
||||
|
||||
String headers = MakeResponseHeaders(HttpOK, contentTypeHeader, bytesRead, keepAlive);
|
||||
_responseLog.Headers = headers;
|
||||
_responseLog.StatusCode = HttpOK;
|
||||
_socket.Send(Encoding.UTF8.GetBytes(headers));
|
||||
|
||||
_socket.Send(fileBytes, 0, bytesRead, SocketFlags.None);
|
||||
|
||||
completed = true;
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!keepAlive || !completed)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
if (fs != null)
|
||||
{
|
||||
fs.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteEntireResponseFromString(int statusCode, String extraHeaders, String body, bool keepAlive)
|
||||
{
|
||||
try
|
||||
{
|
||||
int bodyLength = (body != null) ? Encoding.UTF8.GetByteCount(body) : 0;
|
||||
string headers = MakeResponseHeaders(statusCode, extraHeaders, bodyLength, keepAlive);
|
||||
|
||||
_responseLog.Headers = headers;
|
||||
_responseLog.StatusCode = statusCode;
|
||||
_socket.Send(Encoding.UTF8.GetBytes(headers + body));
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!keepAlive)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteErrorAndClose(int statusCode, string message)
|
||||
{
|
||||
WriteEntireResponseFromString(statusCode, null, GetErrorResponseBody(statusCode, message), false);
|
||||
}
|
||||
|
||||
public void WriteErrorAndClose(int statusCode)
|
||||
{
|
||||
WriteErrorAndClose(statusCode, null);
|
||||
}
|
||||
|
||||
public void WriteErrorWithExtraHeadersAndKeepAlive(int statusCode, string extraHeaders)
|
||||
{
|
||||
WriteEntireResponseFromString(statusCode, extraHeaders, GetErrorResponseBody(statusCode, null), true);
|
||||
}
|
||||
|
||||
public void WriteHeaders(int statusCode, String extraHeaders)
|
||||
{
|
||||
string headers = MakeResponseHeaders(statusCode, extraHeaders, -1, false);
|
||||
|
||||
_responseLog.Headers = headers;
|
||||
_responseLog.StatusCode = statusCode;
|
||||
|
||||
try
|
||||
{
|
||||
_socket.Send(Encoding.UTF8.GetBytes(headers));
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void FinalizeLogInfo()
|
||||
{
|
||||
try
|
||||
{
|
||||
_responseLog.Body = _responseContent.ToArray();
|
||||
_responseContent.Dispose();
|
||||
_responseLog.Created = DateTime.Now;
|
||||
_responseLog.Url = _requestLog.Url;
|
||||
_responseLog.PathTranslated = _requestLog.PathTranslated;
|
||||
_responseLog.Identity = _requestLog.Identity;
|
||||
_responseLog.PhysicalPath = _requestLog.PhysicalPath;
|
||||
}
|
||||
// ReSharper disable EmptyGeneralCatchClause
|
||||
catch
|
||||
// ReSharper restore EmptyGeneralCatchClause
|
||||
{
|
||||
// log error to text
|
||||
}
|
||||
}
|
||||
|
||||
private string GetErrorResponseBody(int statusCode, string message)
|
||||
{
|
||||
string body = Messages.FormatErrorMessageBody(statusCode, _server.VirtualPath);
|
||||
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
{
|
||||
body += "\r\n<!--\r\n" + message + "\r\n-->";
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
private void InitializeLogInfo()
|
||||
{
|
||||
_requestLog = new LogInfo
|
||||
{
|
||||
Created = DateTime.Now,
|
||||
ConversationId = Id,
|
||||
RowType = 1,
|
||||
Identity = _server.GetProcessUser(),
|
||||
PhysicalPath = _server.PhysicalPath
|
||||
};
|
||||
|
||||
_responseLog = new LogInfo
|
||||
{
|
||||
ConversationId = Id,
|
||||
RowType = 2
|
||||
};
|
||||
}
|
||||
|
||||
private static string MakeResponseHeaders(int statusCode, string moreHeaders, int contentLength, bool keepAlive)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.Append("HTTP/1.1 " + statusCode + " " + HttpWorkerRequest.GetStatusDescription(statusCode) + "\r\n");
|
||||
sb.Append("Server: Cassini/" + Messages.VersionString + "\r\n");
|
||||
sb.Append("Date: " + DateTime.Now.ToUniversalTime().ToString("R", DateTimeFormatInfo.InvariantInfo) + "\r\n");
|
||||
|
||||
if (contentLength >= 0)
|
||||
{
|
||||
sb.Append("Content-Length: " + contentLength + "\r\n");
|
||||
}
|
||||
|
||||
if (moreHeaders != null)
|
||||
{
|
||||
sb.Append(moreHeaders);
|
||||
}
|
||||
|
||||
if (!keepAlive)
|
||||
{
|
||||
sb.Append("Connection: Close\r\n");
|
||||
}
|
||||
|
||||
sb.Append("\r\n");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
296
CassiniDev/Core/Host.cs
Normal file
296
CassiniDev/Core/Host.cs
Normal file
|
@ -0,0 +1,296 @@
|
|||
// **********************************************************************************
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
164
CassiniDev/Core/Messages.cs
Normal file
164
CassiniDev/Core/Messages.cs
Normal file
|
@ -0,0 +1,164 @@
|
|||
// **********************************************************************************
|
||||
// CassiniDev - http://cassinidev.codeplex.com
|
||||
//
|
||||
// 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.IO;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace CassiniDev
|
||||
{
|
||||
/// <summary>
|
||||
/// TODO: get this into resources
|
||||
/// </summary>
|
||||
internal static class Messages
|
||||
{
|
||||
private const string _dirListingDirFormat =
|
||||
@"{0,38:dddd, MMMM dd, yyyy hh:mm tt} <dir> <A href=""{1}/"">{2}</A>
|
||||
";
|
||||
|
||||
private const string _dirListingFileFormat =
|
||||
@"{0,38:dddd, MMMM dd, yyyy hh:mm tt} {1,12:n0} <A href=""{2}"">{3}</A>
|
||||
";
|
||||
|
||||
private const string _dirListingFormat1 =
|
||||
@"<html>
|
||||
<head>
|
||||
<title>Directory Listing -- {0}</title>
|
||||
";
|
||||
|
||||
private const string _dirListingFormat2 =
|
||||
@" </head>
|
||||
<body bgcolor=""white"">
|
||||
|
||||
<h2> <i>Directory Listing -- {0}</i> </h2></span>
|
||||
|
||||
<hr width=100% size=1 color=silver>
|
||||
|
||||
<PRE>
|
||||
";
|
||||
|
||||
private const string _dirListingParentFormat =
|
||||
@"<A href=""{0}"">[To Parent Directory]</A>
|
||||
|
||||
";
|
||||
|
||||
private const string _httpErrorFormat1 =
|
||||
@"<html>
|
||||
<head>
|
||||
<title>{0}</title>
|
||||
";
|
||||
|
||||
private const string _httpStyle =
|
||||
@" <style>
|
||||
body {font-family:""Verdana"";font-weight:normal;font-size: 8pt;color:black;}
|
||||
p {font-family:""Verdana"";font-weight:normal;color:black;margin-top: -5px}
|
||||
b {font-family:""Verdana"";font-weight:bold;color:black;margin-top: -5px}
|
||||
h1 { font-family:""Verdana"";font-weight:normal;font-size:18pt;color:red }
|
||||
h2 { font-family:""Verdana"";font-weight:normal;font-size:14pt;color:maroon }
|
||||
pre {font-family:""Lucida Console"";font-size: 8pt}
|
||||
.marker {font-weight: bold; color: black;text-decoration: none;}
|
||||
.version {color: gray;}
|
||||
.error {margin-bottom: 10px;}
|
||||
.expandable { text-decoration:underline; font-weight:bold; color:navy; cursor:hand; }
|
||||
</style>
|
||||
";
|
||||
|
||||
private static readonly string _dirListingTail =
|
||||
@"</PRE>
|
||||
<hr width=100% size=1 color=silver>
|
||||
|
||||
<b>Version Information:</b> Cassini Web Server " +
|
||||
VersionString + @"
|
||||
|
||||
</font>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
";
|
||||
|
||||
private static readonly string _httpErrorFormat2 =
|
||||
@" </head>
|
||||
<body bgcolor=""white"">
|
||||
|
||||
<span><h1>Server Error in '{0}' Application.<hr width=100% size=1 color=silver></h1>
|
||||
|
||||
<h2> <i>HTTP Error {1} - {2}.</i> </h2></span>
|
||||
|
||||
<hr width=100% size=1 color=silver>
|
||||
|
||||
<b>Version Information:</b> Cassini Web Server " +
|
||||
VersionString + @"
|
||||
|
||||
</font>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
";
|
||||
|
||||
public static string VersionString = typeof (Server).Assembly.GetName().Version.ToString();
|
||||
|
||||
public static string FormatDirectoryListing(string dirPath, string parentPath, FileSystemInfo[] elements)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.Append(string.Format(_dirListingFormat1, dirPath));
|
||||
sb.Append(_httpStyle);
|
||||
sb.Append(string.Format(_dirListingFormat2, dirPath));
|
||||
|
||||
if (parentPath != null)
|
||||
{
|
||||
if (!parentPath.EndsWith("/"))
|
||||
{
|
||||
parentPath += "/";
|
||||
}
|
||||
|
||||
sb.Append(string.Format(_dirListingParentFormat, parentPath));
|
||||
}
|
||||
|
||||
if (elements != null)
|
||||
{
|
||||
for (int i = 0; i < elements.Length; i++)
|
||||
{
|
||||
if (elements[i] is FileInfo)
|
||||
{
|
||||
FileInfo fi = (FileInfo) elements[i];
|
||||
sb.Append(string.Format(_dirListingFileFormat,
|
||||
fi.LastWriteTime, fi.Length, fi.Name, fi.Name));
|
||||
}
|
||||
else if (elements[i] is DirectoryInfo)
|
||||
{
|
||||
DirectoryInfo di = (DirectoryInfo) elements[i];
|
||||
sb.Append(string.Format(_dirListingDirFormat,
|
||||
di.LastWriteTime, di.Name, di.Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sb.Append(_dirListingTail);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string FormatErrorMessageBody(int statusCode, string appName)
|
||||
{
|
||||
string desc = HttpWorkerRequest.GetStatusDescription(statusCode);
|
||||
|
||||
return string.Format(_httpErrorFormat1, desc)
|
||||
+ _httpStyle
|
||||
+ string.Format(_httpErrorFormat2, appName, statusCode, desc);
|
||||
}
|
||||
}
|
||||
}
|
179
CassiniDev/Core/NtlmAuth.cs
Normal file
179
CassiniDev/Core/NtlmAuth.cs
Normal file
|
@ -0,0 +1,179 @@
|
|||
// **********************************************************************************
|
||||
// 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.Security;
|
||||
using System.Security.Principal;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace CassiniDev
|
||||
{
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
internal sealed class NtlmAuth : IDisposable
|
||||
{
|
||||
private readonly bool _credentialsHandleAcquired;
|
||||
|
||||
private string _blob;
|
||||
|
||||
private bool _completed;
|
||||
|
||||
private Interop.SecHandle _credentialsHandle;
|
||||
|
||||
private Interop.SecBuffer _inputBuffer;
|
||||
private Interop.SecBufferDesc _inputBufferDesc;
|
||||
|
||||
private Interop.SecBuffer _outputBuffer;
|
||||
|
||||
private Interop.SecBufferDesc _outputBufferDesc;
|
||||
|
||||
private Interop.SecHandle _securityContext;
|
||||
|
||||
private bool _securityContextAcquired;
|
||||
|
||||
private uint _securityContextAttributes;
|
||||
|
||||
private SecurityIdentifier _sid;
|
||||
|
||||
private long _timestamp;
|
||||
|
||||
public NtlmAuth()
|
||||
{
|
||||
if (
|
||||
Interop.AcquireCredentialsHandle(null, "NTLM", 1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero,
|
||||
ref _credentialsHandle, ref _timestamp) != 0)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
_credentialsHandleAcquired = true;
|
||||
}
|
||||
|
||||
public string Blob
|
||||
{
|
||||
get { return _blob; }
|
||||
}
|
||||
|
||||
public bool Completed
|
||||
{
|
||||
get { return _completed; }
|
||||
}
|
||||
|
||||
public SecurityIdentifier SID
|
||||
{
|
||||
get { return _sid; }
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
FreeUnmanagedResources();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public unsafe bool Authenticate(string blobString)
|
||||
{
|
||||
_blob = null;
|
||||
byte[] buffer = Convert.FromBase64String(blobString);
|
||||
byte[] inArray = new byte[0x4000];
|
||||
fixed (void* ptrRef = &_securityContext)
|
||||
{
|
||||
fixed (void* ptrRef2 = &_inputBuffer)
|
||||
{
|
||||
fixed (void* ptrRef3 = &_outputBuffer)
|
||||
{
|
||||
fixed (void* ptrRef4 = buffer)
|
||||
{
|
||||
fixed (void* ptrRef5 = inArray)
|
||||
{
|
||||
IntPtr zero = IntPtr.Zero;
|
||||
if (_securityContextAcquired)
|
||||
{
|
||||
zero = (IntPtr) ptrRef;
|
||||
}
|
||||
_inputBufferDesc.ulVersion = 0;
|
||||
_inputBufferDesc.cBuffers = 1;
|
||||
_inputBufferDesc.pBuffers = (IntPtr) ptrRef2;
|
||||
_inputBuffer.cbBuffer = (uint) buffer.Length;
|
||||
_inputBuffer.BufferType = 2;
|
||||
_inputBuffer.pvBuffer = (IntPtr) ptrRef4;
|
||||
_outputBufferDesc.ulVersion = 0;
|
||||
_outputBufferDesc.cBuffers = 1;
|
||||
_outputBufferDesc.pBuffers = (IntPtr) ptrRef3;
|
||||
_outputBuffer.cbBuffer = (uint) inArray.Length;
|
||||
_outputBuffer.BufferType = 2;
|
||||
_outputBuffer.pvBuffer = (IntPtr) ptrRef5;
|
||||
int num = Interop.AcceptSecurityContext(ref _credentialsHandle, zero,
|
||||
ref _inputBufferDesc, 20,
|
||||
0, ref _securityContext, ref _outputBufferDesc,
|
||||
ref _securityContextAttributes, ref _timestamp);
|
||||
if (num == 0x90312)
|
||||
{
|
||||
_securityContextAcquired = true;
|
||||
_blob = Convert.ToBase64String(inArray, 0, (int) _outputBuffer.cbBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (num != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
IntPtr phToken = IntPtr.Zero;
|
||||
if (Interop.QuerySecurityContextToken(ref _securityContext, ref phToken) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
using (WindowsIdentity identity = new WindowsIdentity(phToken))
|
||||
{
|
||||
_sid = identity.User;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interop.CloseHandle(phToken);
|
||||
}
|
||||
_completed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
~NtlmAuth()
|
||||
{
|
||||
FreeUnmanagedResources();
|
||||
}
|
||||
|
||||
private void FreeUnmanagedResources()
|
||||
{
|
||||
if (_securityContextAcquired)
|
||||
{
|
||||
Interop.DeleteSecurityContext(ref _securityContext);
|
||||
}
|
||||
if (_credentialsHandleAcquired)
|
||||
{
|
||||
Interop.FreeCredentialsHandle(ref _credentialsHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1405
CassiniDev/Core/Request.cs
Normal file
1405
CassiniDev/Core/Request.cs
Normal file
File diff suppressed because it is too large
Load diff
53
CassiniDev/Core/RequestEventArgs.cs
Normal file
53
CassiniDev/Core/RequestEventArgs.cs
Normal file
|
@ -0,0 +1,53 @@
|
|||
// **********************************************************************************
|
||||
// CassiniDev - http://cassinidev.codeplex.com
|
||||
//
|
||||
// Copyright (c) 2010 Sky Sanders. 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 CassiniDev.ServerLog;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace CassiniDev
|
||||
{
|
||||
public class RequestEventArgs : EventArgs
|
||||
{
|
||||
private readonly Guid _id;
|
||||
|
||||
private readonly LogInfo _requestLog;
|
||||
|
||||
private readonly LogInfo _responseLog;
|
||||
|
||||
public RequestEventArgs(Guid id, LogInfo requestLog, LogInfo responseLog)
|
||||
{
|
||||
_requestLog = requestLog;
|
||||
_responseLog = responseLog;
|
||||
_id = id;
|
||||
}
|
||||
|
||||
public Guid Id
|
||||
{
|
||||
get { return _id; }
|
||||
}
|
||||
|
||||
public LogInfo RequestLog
|
||||
{
|
||||
get { return _requestLog; }
|
||||
}
|
||||
|
||||
public LogInfo ResponseLog
|
||||
{
|
||||
get { return _responseLog; }
|
||||
}
|
||||
}
|
||||
}
|
523
CassiniDev/Core/Server.cs
Normal file
523
CassiniDev/Core/Server.cs
Normal file
|
@ -0,0 +1,523 @@
|
|||
// **********************************************************************************
|
||||
// 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.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Remoting;
|
||||
using System.Security.Permissions;
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using System.Web.Hosting;
|
||||
using CassiniDev.ServerLog;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace CassiniDev
|
||||
{
|
||||
[PermissionSet(SecurityAction.LinkDemand, Name = "Everything"),
|
||||
PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")]
|
||||
public class Server : MarshalByRefObject, IDisposable
|
||||
{
|
||||
private readonly ApplicationManager _appManager;
|
||||
|
||||
private readonly bool _disableDirectoryListing;
|
||||
|
||||
private readonly string _hostName;
|
||||
|
||||
private readonly IPAddress _ipAddress;
|
||||
|
||||
private readonly object _lockObject;
|
||||
|
||||
private readonly string _physicalPath;
|
||||
|
||||
private readonly int _port;
|
||||
private readonly bool _requireAuthentication;
|
||||
private readonly int _timeoutInterval;
|
||||
private readonly string _virtualPath;
|
||||
private bool _disposed;
|
||||
|
||||
private Host _host;
|
||||
|
||||
private IntPtr _processToken;
|
||||
|
||||
private string _processUser;
|
||||
|
||||
private int _requestCount;
|
||||
|
||||
private bool _shutdownInProgress;
|
||||
|
||||
private Socket _socket;
|
||||
|
||||
private Timer _timer;
|
||||
|
||||
public Server(int port, string virtualPath, string physicalPath)
|
||||
: this(port, virtualPath, physicalPath, false, false)
|
||||
{
|
||||
}
|
||||
|
||||
public Server(int port, string physicalPath)
|
||||
: this(port, "/", physicalPath, IPAddress.Loopback)
|
||||
{
|
||||
}
|
||||
|
||||
public Server(string physicalPath)
|
||||
: this(CassiniNetworkUtils.GetAvailablePort(32768, 65535, IPAddress.Loopback, false), physicalPath)
|
||||
{
|
||||
}
|
||||
|
||||
public Server(int port, string virtualPath, string physicalPath, IPAddress ipAddress, string hostName,
|
||||
int timeout, bool requireAuthentication)
|
||||
: this(port, virtualPath, physicalPath, ipAddress, hostName, timeout, requireAuthentication, false)
|
||||
{
|
||||
}
|
||||
|
||||
public Server(int port, string virtualPath, string physicalPath, bool requireAuthentication)
|
||||
: this(port, virtualPath, physicalPath, requireAuthentication, false)
|
||||
{
|
||||
}
|
||||
|
||||
public Server(int port, string virtualPath, string physicalPath, IPAddress ipAddress, string hostName)
|
||||
: this(port, virtualPath, physicalPath, ipAddress, hostName, 0, false, false)
|
||||
{
|
||||
}
|
||||
|
||||
public Server(int port, string virtualPath, string physicalPath, IPAddress ipAddress, string hostName,
|
||||
int timeout, bool requireAuthentication, bool disableDirectoryListing)
|
||||
: this(port, virtualPath, physicalPath, requireAuthentication, disableDirectoryListing)
|
||||
{
|
||||
_ipAddress = ipAddress;
|
||||
_hostName = hostName;
|
||||
_timeoutInterval = timeout;
|
||||
}
|
||||
|
||||
public Server(int port, string virtualPath, string physicalPath, IPAddress ipAddress)
|
||||
: this(port, virtualPath, physicalPath, ipAddress, null, 0, false, false)
|
||||
{
|
||||
}
|
||||
|
||||
public Server(int port, string virtualPath, string physicalPath, bool requireAuthentication,
|
||||
bool disableDirectoryListing)
|
||||
{
|
||||
_ipAddress = IPAddress.Loopback;
|
||||
_requireAuthentication = requireAuthentication;
|
||||
_disableDirectoryListing = disableDirectoryListing;
|
||||
_lockObject = new object();
|
||||
_port = port;
|
||||
_virtualPath = virtualPath;
|
||||
_physicalPath = Path.GetFullPath(physicalPath);
|
||||
_physicalPath = _physicalPath.EndsWith("\\", StringComparison.Ordinal)
|
||||
? _physicalPath
|
||||
: _physicalPath + "\\";
|
||||
_appManager = ApplicationManager.GetApplicationManager();
|
||||
ObtainProcessToken();
|
||||
}
|
||||
|
||||
public Server(string physicalPath, bool requireAuthentication)
|
||||
: this(
|
||||
CassiniNetworkUtils.GetAvailablePort(32768, 65535, IPAddress.Loopback, false), "/", physicalPath,
|
||||
requireAuthentication)
|
||||
{
|
||||
}
|
||||
|
||||
public Server(int port, string virtualPath, string physicalPath, IPAddress ipAddress, string hostName,
|
||||
int timeout)
|
||||
: this(port, virtualPath, physicalPath, ipAddress, hostName, timeout, false, false)
|
||||
{
|
||||
}
|
||||
|
||||
public bool DisableDirectoryListing
|
||||
{
|
||||
get { return _disableDirectoryListing; }
|
||||
}
|
||||
|
||||
public bool RequireAuthentication
|
||||
{
|
||||
get { return _requireAuthentication; }
|
||||
}
|
||||
|
||||
public int TimeoutInterval
|
||||
{
|
||||
get { return _timeoutInterval; }
|
||||
}
|
||||
|
||||
public string HostName
|
||||
{
|
||||
get { return _hostName; }
|
||||
}
|
||||
|
||||
public IPAddress IPAddress
|
||||
{
|
||||
get { return _ipAddress; }
|
||||
}
|
||||
|
||||
public string PhysicalPath
|
||||
{
|
||||
get { return _physicalPath; }
|
||||
}
|
||||
|
||||
public int Port
|
||||
{
|
||||
get { return _port; }
|
||||
}
|
||||
|
||||
public string RootUrl
|
||||
{
|
||||
get
|
||||
{
|
||||
string hostname = _hostName;
|
||||
if (string.IsNullOrEmpty(_hostName))
|
||||
{
|
||||
if (_ipAddress.Equals(IPAddress.Loopback) || _ipAddress.Equals(IPAddress.IPv6Loopback) ||
|
||||
_ipAddress.Equals(IPAddress.Any) || _ipAddress.Equals(IPAddress.IPv6Any))
|
||||
{
|
||||
hostname = "localhost";
|
||||
}
|
||||
else
|
||||
{
|
||||
hostname = _ipAddress.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
return _port != 80
|
||||
?
|
||||
String.Format("http://{0}:{1}{2}", hostname, _port, _virtualPath)
|
||||
:
|
||||
//FIX: #12017 - TODO:TEST
|
||||
string.Format("http://{0}{1}", hostname, _virtualPath);
|
||||
}
|
||||
}
|
||||
|
||||
public string VirtualPath
|
||||
{
|
||||
get { return _virtualPath; }
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
ShutDown();
|
||||
}
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public event EventHandler<RequestEventArgs> RequestComplete;
|
||||
|
||||
public event EventHandler TimedOut;
|
||||
|
||||
public IntPtr GetProcessToken()
|
||||
{
|
||||
return _processToken;
|
||||
}
|
||||
|
||||
public string GetProcessUser()
|
||||
{
|
||||
return _processUser;
|
||||
}
|
||||
|
||||
public void HostStopped()
|
||||
{
|
||||
_host = null;
|
||||
}
|
||||
|
||||
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
|
||||
public override object InitializeLifetimeService()
|
||||
{
|
||||
// never expire the license
|
||||
return null;
|
||||
}
|
||||
|
||||
// called at the end of request processing
|
||||
// to disconnect the remoting proxy for Connection object
|
||||
// and allow GC to pick it up
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="conn"></param>
|
||||
public void OnRequestEnd(Connection conn)
|
||||
{
|
||||
try
|
||||
{
|
||||
OnRequestComplete(conn.Id, conn.RequestLog.Clone(), conn.ResponseLog.Clone());
|
||||
}
|
||||
catch
|
||||
{
|
||||
// swallow - we don't want consumer killing the server
|
||||
}
|
||||
RemotingServices.Disconnect(conn);
|
||||
DecrementRequestCount();
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
_socket = CreateSocketBindAndListen(AddressFamily.InterNetwork, _ipAddress, _port);
|
||||
|
||||
//start the timer
|
||||
DecrementRequestCount();
|
||||
|
||||
ThreadPool.QueueUserWorkItem(delegate
|
||||
{
|
||||
while (!_shutdownInProgress)
|
||||
{
|
||||
try
|
||||
{
|
||||
Socket acceptedSocket = _socket.Accept();
|
||||
|
||||
ThreadPool.QueueUserWorkItem(delegate
|
||||
{
|
||||
if (!_shutdownInProgress)
|
||||
{
|
||||
Connection conn = new Connection(this, acceptedSocket);
|
||||
|
||||
if (conn.WaitForRequestBytes() == 0)
|
||||
{
|
||||
conn.WriteErrorAndClose(400);
|
||||
return;
|
||||
}
|
||||
|
||||
Host host = GetHost();
|
||||
|
||||
if (host == null)
|
||||
{
|
||||
conn.WriteErrorAndClose(500);
|
||||
return;
|
||||
}
|
||||
|
||||
IncrementRequestCount();
|
||||
host.ProcessRequest(conn);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
~Server()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
|
||||
private static Socket CreateSocketBindAndListen(AddressFamily family, IPAddress address, int port)
|
||||
{
|
||||
Socket socket = new Socket(family, SocketType.Stream, ProtocolType.Tcp);
|
||||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||
socket.Bind(new IPEndPoint(address, port));
|
||||
socket.Listen((int) SocketOptionName.MaxConnections);
|
||||
return socket;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="virtualPath"></param>
|
||||
/// <param name="physicalPath"></param>
|
||||
/// <param name="hostType"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>This is Dmitry's hack to enable running outside of GAC</remarks>
|
||||
private object CreateWorkerAppDomainWithHost(string virtualPath, string physicalPath, Type hostType)
|
||||
{
|
||||
// this creates worker app domain in a way that host doesn't need to be in GAC or bin
|
||||
// using BuildManagerHost via private reflection
|
||||
string uniqueAppString = string.Concat(virtualPath, physicalPath).ToLowerInvariant();
|
||||
string appId = (uniqueAppString.GetHashCode()).ToString("x", CultureInfo.InvariantCulture);
|
||||
|
||||
// create BuildManagerHost in the worker app domain
|
||||
//ApplicationManager appManager = ApplicationManager.GetApplicationManager();
|
||||
Type buildManagerHostType = typeof (HttpRuntime).Assembly.GetType("System.Web.Compilation.BuildManagerHost");
|
||||
IRegisteredObject buildManagerHost = _appManager.CreateObject(appId, buildManagerHostType, virtualPath,
|
||||
physicalPath, false);
|
||||
|
||||
// call BuildManagerHost.RegisterAssembly to make Host type loadable in the worker app domain
|
||||
buildManagerHostType.InvokeMember("RegisterAssembly",
|
||||
BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic,
|
||||
null,
|
||||
buildManagerHost,
|
||||
new object[] {hostType.Assembly.FullName, hostType.Assembly.Location});
|
||||
|
||||
// create Host in the worker app domain
|
||||
return _appManager.CreateObject(appId, hostType, virtualPath, physicalPath, false);
|
||||
}
|
||||
|
||||
private void DecrementRequestCount()
|
||||
{
|
||||
_requestCount--;
|
||||
|
||||
if (_requestCount < 1)
|
||||
{
|
||||
_requestCount = 0;
|
||||
|
||||
if (_timeoutInterval > 0)
|
||||
{
|
||||
_timer = new Timer(TimeOut, null, _timeoutInterval, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Host GetHost()
|
||||
{
|
||||
if (_shutdownInProgress)
|
||||
return null;
|
||||
Host host = _host;
|
||||
if (host == null)
|
||||
{
|
||||
#if NET40
|
||||
object obj2 = new object();
|
||||
bool flag = false;
|
||||
try
|
||||
{
|
||||
Monitor.Enter(obj2 = _lockObject, ref flag);
|
||||
host = _host;
|
||||
if (host == null)
|
||||
{
|
||||
host = (Host)CreateWorkerAppDomainWithHost(_virtualPath, _physicalPath, typeof(Host));
|
||||
host.Configure(this, _port, _virtualPath, _physicalPath, _requireAuthentication, _disableDirectoryListing);
|
||||
_host = host;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
Monitor.Exit(obj2);
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
lock (_lockObject)
|
||||
{
|
||||
host = _host;
|
||||
if (host == null)
|
||||
{
|
||||
host = (Host) CreateWorkerAppDomainWithHost(_virtualPath, _physicalPath, typeof (Host));
|
||||
host.Configure(this, _port, _virtualPath, _physicalPath, _requireAuthentication,
|
||||
_disableDirectoryListing);
|
||||
_host = host;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
return host;
|
||||
}
|
||||
|
||||
private void IncrementRequestCount()
|
||||
{
|
||||
_requestCount++;
|
||||
_timer = null;
|
||||
}
|
||||
|
||||
|
||||
private void ObtainProcessToken()
|
||||
{
|
||||
if (Interop.ImpersonateSelf(2))
|
||||
{
|
||||
Interop.OpenThreadToken(Interop.GetCurrentThread(), 0xf01ff, true, ref _processToken);
|
||||
Interop.RevertToSelf();
|
||||
// ReSharper disable PossibleNullReferenceException
|
||||
_processUser = WindowsIdentity.GetCurrent().Name;
|
||||
// ReSharper restore PossibleNullReferenceException
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRequestComplete(Guid id, LogInfo requestLog, LogInfo responseLog)
|
||||
{
|
||||
EventHandler<RequestEventArgs> complete = RequestComplete;
|
||||
|
||||
if (complete != null)
|
||||
{
|
||||
complete(this, new RequestEventArgs(id, requestLog, responseLog));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void ShutDown()
|
||||
{
|
||||
_shutdownInProgress = true;
|
||||
|
||||
try
|
||||
{
|
||||
if (_socket != null)
|
||||
{
|
||||
_socket.Close();
|
||||
}
|
||||
}
|
||||
// ReSharper disable EmptyGeneralCatchClause
|
||||
catch
|
||||
// ReSharper restore EmptyGeneralCatchClause
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
_socket = null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (_host != null)
|
||||
{
|
||||
_host.Shutdown();
|
||||
}
|
||||
|
||||
while (_host != null)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
// ReSharper disable EmptyGeneralCatchClause
|
||||
catch
|
||||
// ReSharper restore EmptyGeneralCatchClause
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
_host = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void TimeOut(object ignored)
|
||||
{
|
||||
TimeOut();
|
||||
}
|
||||
|
||||
public void TimeOut()
|
||||
{
|
||||
ShutDown();
|
||||
OnTimeOut();
|
||||
}
|
||||
|
||||
private void OnTimeOut()
|
||||
{
|
||||
EventHandler handler = TimedOut;
|
||||
if (handler != null) handler(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue