From: <mis...@us...> - 2007-03-09 22:37:32
|
Revision: 34 http://svn.sourceforge.net/mp-webinterface/?rev=34&view=rev Author: misterd_sf Date: 2007-03-09 14:37:29 -0800 (Fri, 09 Mar 2007) Log Message: ----------- Commented WebServer and windows service Modified Paths: -------------- trunk/Version2/WebServer/ByteParser.cs trunk/Version2/WebServer/ByteString.cs trunk/Version2/WebServer/Connection.cs trunk/Version2/WebServer/Host.cs trunk/Version2/WebServer/Messages.cs trunk/Version2/WebServer/Request.cs trunk/Version2/WebServer/Server.cs trunk/Version2/WebServer/ServerConfiguration.cs trunk/Version2/WebServer/WebServer.csproj trunk/Version2/WindowsService/DebugServiceControlForm.Designer.cs trunk/Version2/WindowsService/DebugServiceControlForm.cs trunk/Version2/WindowsService/Program.cs trunk/Version2/WindowsService/ProjectInstaller.Designer.cs trunk/Version2/WindowsService/ProjectInstaller.cs trunk/Version2/WindowsService/Service.Designer.cs trunk/Version2/WindowsService/Service.cs trunk/Version2/WindowsService/WindowsService.csproj Modified: trunk/Version2/WebServer/ByteParser.cs =================================================================== --- trunk/Version2/WebServer/ByteParser.cs 2007-03-09 17:45:37 UTC (rev 33) +++ trunk/Version2/WebServer/ByteParser.cs 2007-03-09 22:37:29 UTC (rev 34) @@ -11,15 +11,31 @@ using System.Text; namespace MPW.WebServer { - + /// <summary> + /// Internal class for byte parsing + /// </summary> internal class ByteParser { #region variables + /// <summary> + /// Bytes + /// </summary> private byte[] _bytes; + /// <summary> + /// Position + /// </summary> private int _pos; + /// <summary> + /// Server + /// </summary> private Server _server; #endregion #region ctor + /// <summary> + /// Constructor + /// </summary> + /// <param name="bytes">Bytes to parse</param> + /// <param name="server">Server object</param> internal ByteParser(byte[] bytes, Server server) { _bytes = bytes; _pos = 0; @@ -28,6 +44,9 @@ #endregion #region properties + /// <summary> + /// Gets the current offset + /// </summary> internal int CurrentOffset { get { @@ -37,6 +56,10 @@ #endregion #region internal methods + /// <summary> + /// Read a line from the byte array + /// </summary> + /// <returns>Byte string</returns> internal ByteString ReadLine() { ByteString line = null; Modified: trunk/Version2/WebServer/ByteString.cs =================================================================== --- trunk/Version2/WebServer/ByteString.cs 2007-03-09 17:45:37 UTC (rev 33) +++ trunk/Version2/WebServer/ByteString.cs 2007-03-09 22:37:29 UTC (rev 34) @@ -11,15 +11,32 @@ using System.Text; namespace MPW.WebServer { - + /// <summary> + /// Internal class for a byte string + /// </summary> internal class ByteString { #region variables + /// <summary> + /// Bytes + /// </summary> private byte[] _bytes; + /// <summary> + /// Offset + /// </summary> private int _offset; + /// <summary> + /// Length + /// </summary> private int _length; #endregion #region ctor + /// <summary> + /// Constructor + /// </summary> + /// <param name="bytes">Bytes</param> + /// <param name="offset">Offset</param> + /// <param name="length">Length</param> public ByteString(byte[] bytes, int offset, int length) { _bytes = bytes; _offset = offset; @@ -28,30 +45,47 @@ #endregion #region properties + /// <summary> + /// Gets the bytes of the bytestring + /// </summary> public byte[] Bytes { get { return _bytes; } } + /// <summary> + /// Is Byte string empty? + /// </summary> public bool IsEmpty { get { return (_bytes == null || _length == 0); } } + /// <summary> + /// Length of the byte string + /// </summary> public int Length { get { return _length; } } - + + /// <summary> + /// Gets the offset of the byte string + /// </summary> public int Offset { get { return _offset; } } + /// <summary> + /// Gets a single byte of the byte string + /// </summary> + /// <param name="index">Index of the byte</param> + /// <returns>Single byte of the byte string</returns> public byte this[int index] { get { return _bytes[_offset+index]; @@ -60,10 +94,19 @@ #endregion #region public methods + /// <summary> + /// Returns the String of the byte string + /// </summary> + /// <returns>String of the byte string (UTF-8 encoding)</returns> public String GetString() { return GetString(Encoding.UTF8); } + /// <summary> + /// Returns the String of the byte string in the given encoding + /// </summary> + /// <param name="enc">Encoding of the String</param> + /// <returns>String of the byte string in the given encoding</returns> public String GetString(Encoding enc) { if (IsEmpty) { return String.Empty; @@ -71,6 +114,10 @@ return enc.GetString(_bytes, _offset, _length); } + /// <summary> + /// Returns a copy of the byte array + /// </summary> + /// <returns>Byte array copy</returns> public byte[] GetBytes() { byte[] bytes = new byte[_length]; if (_length > 0) { @@ -79,10 +126,21 @@ return bytes; } + /// <summary> + /// Index of a single character + /// </summary> + /// <param name="ch">Character</param> + /// <returns>Index of the character</returns> public int IndexOf(char ch) { return IndexOf(ch, 0); } + /// <summary> + /// Index of a single character + /// </summary> + /// <param name="ch">Character</param> + /// <param name="offset">Offset</param> + /// <returns>Index of the character</returns> public int IndexOf(char ch, int offset) { for (int i = offset; i < _length; i++) { if (this[i] == (byte)ch) { @@ -93,14 +151,30 @@ return -1; } + /// <summary> + /// Returns a sub(byte)string + /// </summary> + /// <param name="offset">Offset</param> + /// <returns>Sub(byte)string</returns> public ByteString Substring(int offset) { return Substring(offset, _length-offset); } + /// <summary> + /// Returns a sub(byte)string + /// </summary> + /// <param name="offset">Offset</param> + /// <param name="len">Length</param> + /// <returns>Sub(byte)string</returns> public ByteString Substring(int offset, int len) { return new ByteString(_bytes, _offset+offset, len); } + /// <summary> + /// Splits the byte string + /// </summary> + /// <param name="sep">Seperator character</param> + /// <returns>Array of the byte string</returns> public ByteString[] Split(char sep) { ArrayList list = new ArrayList(); Modified: trunk/Version2/WebServer/Connection.cs =================================================================== --- trunk/Version2/WebServer/Connection.cs 2007-03-09 17:45:37 UTC (rev 33) +++ trunk/Version2/WebServer/Connection.cs 2007-03-09 22:37:29 UTC (rev 34) @@ -20,16 +20,36 @@ using System.Web.Hosting; namespace MPW.WebServer { - + /// <summary> + /// Connection class, that handles all socket related operations + /// </summary> internal class Connection : MarshalByRefObject { #region variables + /// <summary> + /// Web Server + /// </summary> private Server _server; + /// <summary> + /// Socket connection to client + /// </summary> private Socket _socket; + /// <summary> + /// Server configuration + /// </summary> private ServerConfiguration _configuration; + /// <summary> + /// ContentType mapping + /// </summary> private Dictionary<String, String> _contentTypes; #endregion #region ctor + /// <summary> + /// Constructor + /// </summary> + /// <param name="server">Web Server</param> + /// <param name="socket">Socket connection to client</param> + /// <param name="configuration">Server Configuration</param> internal Connection(Server server, Socket socket, ServerConfiguration configuration) { _server = server; _socket = socket; @@ -39,6 +59,10 @@ #endregion #region overrides + /// <summary> + /// Init the lifetime service + /// </summary> + /// <returns></returns> public override object InitializeLifetimeService() { // never expire the license return null; @@ -46,12 +70,21 @@ #endregion #region properties + /// <summary> + /// Status of the socket connection + /// </summary> internal bool Connected { get { - return _socket.Connected; + if (_socket != null) { + return _socket.Connected; + } + return false; } } + /// <summary> + /// Is local connection? + /// </summary> internal bool IsLocal { get { string remoteIP = RemoteIP; @@ -64,6 +97,9 @@ } } + /// <summary> + /// Returns the local IP + /// </summary> internal string LocalIP { get { IPEndPoint endPoint = (IPEndPoint)_socket.LocalEndPoint; @@ -75,6 +111,9 @@ } } + /// <summary> + /// Returns the remote ip + /// </summary> internal string RemoteIP { get { IPEndPoint endPoint = (IPEndPoint)_socket.RemoteEndPoint; @@ -93,21 +132,14 @@ #endregion #region private static methods - private bool CheckLocalServerIP(string remoteIP) { - try { - IPHostEntry hostEntry = Dns.GetHostEntry(Environment.MachineName); - IPAddress localAddress; - for (int i = 0; i < hostEntry.AddressList.Length; i++) { - localAddress = hostEntry.AddressList[i]; - if (remoteIP.Equals(localAddress.ToString())) { - return true; - } - } - } catch (Exception e) { - _server.WriteErrorToLog("Error while checking Server IP.\n", e); - } - return false; - } + /// <summary> + /// Generates the response http header + /// </summary> + /// <param name="statusCode">State headers</param> + /// <param name="moreHeaders">Additional Headers</param> + /// <param name="contentLength">Length of the content</param> + /// <param name="keepAlive">Keep the connection alive</param> + /// <returns>Response header</returns> private static String MakeResponseHeaders(int statusCode, String moreHeaders, int contentLength, bool keepAlive) { StringBuilder sb = new StringBuilder(); @@ -127,6 +159,32 @@ #endregion #region private methods + /// <summary> + /// Checks if the remote IP is a local server IP + /// </summary> + /// <param name="remoteIP">Remote IP</param> + /// <returns>true, if the remote ip is a local server IP; false otherwise</returns> + private bool CheckLocalServerIP(string remoteIP) { + try { + IPHostEntry hostEntry = Dns.GetHostEntry(Environment.MachineName); + IPAddress localAddress; + for (int i = 0; i < hostEntry.AddressList.Length; i++) { + localAddress = hostEntry.AddressList[i]; + if (remoteIP.Equals(localAddress.ToString())) { + return true; + } + } + } catch (Exception e) { + _server.WriteErrorToLog("Error while checking Server IP.\n", e); + } + return false; + } + + /// <summary> + /// Generates the content type header + /// </summary> + /// <param name="fileName">FileName</param> + /// <returns>Content type header</returns> private String MakeContentTypeHeader(String fileName) { Debug.Assert(File.Exists(fileName)); @@ -138,6 +196,12 @@ return null; } + /// <summary> + /// Generates the error response body + /// </summary> + /// <param name="statusCode">Status code of the error</param> + /// <param name="message">Error message</param> + /// <returns>Body of the error response</returns> private string GetErrorResponseBody(int statusCode, string message) { string body = Messages.FormatErrorMessageBody(statusCode, _server.VirtualPath); if (message != null && message.Length > 0) { @@ -148,6 +212,9 @@ #endregion #region internal methods + /// <summary> + /// Close the socket connection + /// </summary> internal void Close() { try { if (_socket != null) { @@ -161,6 +228,11 @@ } } + /// <summary> + /// Reads the request bytes + /// </summary> + /// <param name="maxBytes">Maximum bytes</param> + /// <returns>Request bytes</returns> internal byte[] ReadRequestBytes(int maxBytes) { try { if (WaitForRequestBytes() == 0) { @@ -195,10 +267,19 @@ } } + /// <summary> + /// Writes 100 error response + /// </summary> internal void Write100Continue() { WriteEntireResponseFromString(100, null, null, true); } + /// <summary> + /// Writes Body of the response + /// </summary> + /// <param name="data">Data to send</param> + /// <param name="offset">Offset in the byte array</param> + /// <param name="length">Length to send</param> internal void WriteBody(byte[] data, int offset, int length) { try { _socket.Send(data, offset, length, SocketFlags.None); @@ -207,6 +288,13 @@ } } + /// <summary> + /// Writes Response from String + /// </summary> + /// <param name="statusCode">Status code of the response</param> + /// <param name="extraHeaders">Additional headers</param> + /// <param name="body">Body</param> + /// <param name="keepAlive">Keep the connection alive?</param> internal void WriteEntireResponseFromString(int statusCode, String extraHeaders, String body, bool keepAlive) { try { int bodyLength = (body != null) ? Encoding.UTF8.GetByteCount(body) : 0; @@ -223,6 +311,11 @@ } } + /// <summary> + /// Writes Response from file + /// </summary> + /// <param name="fileName">Filename of the response</param> + /// <param name="keepAlive">Keep the connection alive?</param> internal void WriteEntireResponseFromFile(String fileName, bool keepAlive) { if (!File.Exists(fileName)) { WriteErrorAndClose(404); @@ -264,18 +357,36 @@ } } + /// <summary> + /// Writes the error response and close connection + /// </summary> + /// <param name="statusCode">Status code</param> + /// <param name="message">Error message</param> internal void WriteErrorAndClose(int statusCode, string message) { WriteEntireResponseFromString(statusCode, null, GetErrorResponseBody(statusCode, message), false); } + /// <summary> + /// Writes the error response and close connection + /// </summary> + /// <param name="statusCode">Status code</param> internal void WriteErrorAndClose(int statusCode) { WriteErrorAndClose(statusCode, null); } + /// <summary> + /// Writes error message with additional headers + /// </summary> + /// <param name="statusCode">Status code</param> + /// <param name="extraHeaders">Additional headers</param> internal void WriteErrorWithExtraHeadersAndKeepAlive(int statusCode, string extraHeaders) { WriteEntireResponseFromString(statusCode, extraHeaders, GetErrorResponseBody(statusCode, null), true); } - + + /// <summary> + /// Wait for request bytes + /// </summary> + /// <returns>Available bytes</returns> internal int WaitForRequestBytes() { int availBytes = 0; @@ -296,6 +407,11 @@ return availBytes; } + /// <summary> + /// Writes all headers + /// </summary> + /// <param name="statusCode">Status code</param> + /// <param name="extraHeaders">Additional headers</param> internal void WriteHeaders(int statusCode, String extraHeaders) { string headers = MakeResponseHeaders(statusCode, extraHeaders, -1, false); Modified: trunk/Version2/WebServer/Host.cs =================================================================== --- trunk/Version2/WebServer/Host.cs 2007-03-09 17:45:37 UTC (rev 33) +++ trunk/Version2/WebServer/Host.cs 2007-03-09 22:37:29 UTC (rev 34) @@ -23,27 +23,62 @@ using log4net; namespace MPW.WebServer { - + /// <summary> + /// Hosting environment of the web server + /// </summary> internal class Host : MarshalByRefObject, IRegisteredObject { #region variables + /// <summary> + /// Server + /// </summary> private Server _server; + + /// <summary> + /// Server configuration + /// </summary> private ServerConfiguration _configuration; + /// <summary> + /// Pending calls + /// </summary> private volatile int _pendingCallsCount; + + /// <summary> + /// Virtual path (lower case) + /// </summary> private string _lowerCasedVirtualPath; + + /// <summary> + /// Virtaul path with trailing slash (lower case) + /// </summary> private string _lowerCasedVirtualPathWithTrailingSlash; + + /// <summary> + /// Physical Client script path + /// </summary> private string _physicalClientScriptPath; + + /// <summary> + /// Client script path with trailing slash (lower case) + /// </summary> private string _lowerCasedClientScriptPathWithTrailingSlash; #endregion #region ctor + /// <summary> + /// Constructor and register this object as hosting environment + /// </summary> public Host() { HostingEnvironment.RegisterObject(this); } #endregion #region overrides + /// <summary> + /// Never expire the license + /// </summary> + /// <returns></returns> public override object InitializeLifetimeService() { // never expire the license return null; @@ -51,6 +86,11 @@ #endregion #region public methods + /// <summary> + /// Configure the hosting environment + /// </summary> + /// <param name="server">Server</param> + /// <param name="configuration">Server configuration</param> public void Configure(Server server, ServerConfiguration configuration) { _server = server; _configuration = configuration; @@ -62,6 +102,10 @@ _lowerCasedClientScriptPathWithTrailingSlash = CultureInfo.InvariantCulture.TextInfo.ToLower(HttpRuntime.AspClientScriptVirtualPath + "/"); } + /// <summary> + /// Process request + /// </summary> + /// <param name="conn">Connection of the request</param> public void ProcessRequest(Connection conn) { // Add a pending call to make sure our thread doesn't get killed AddPendingCall(); @@ -76,15 +120,29 @@ } } + /// <summary> + /// Shutdown the hosting environment + /// </summary> public void Shutdown() { HostingEnvironment.InitiateShutdown(); } + /// <summary> + /// Is virtual path in application path + /// </summary> + /// <param name="path">Virtual path</param> + /// <returns>true, if in virutal path; false otherwise</returns> public bool IsVirtualPathInApp(String path) { bool isClientScriptPath; return IsVirtualPathInApp(path, out isClientScriptPath); } + /// <summary> + /// Is virtual path in application path + /// </summary> + /// <param name="path">Virtual path</param> + /// <param name="isClientScriptPath">Output, if is client script path</param> + /// <returns>true, if in virutal path; false otherwise</returns> public bool IsVirtualPathInApp(String path, out bool isClientScriptPath) { isClientScriptPath = false; @@ -116,6 +174,11 @@ return false; } + /// <summary> + /// Is virtual path a application path + /// </summary> + /// <param name="path">Virtual path</param> + /// <returns>true, if a application path; false otherwise</returns> public bool IsVirtualPathAppPath(String path) { if (path == null) { return false; @@ -128,6 +191,9 @@ #endregion #region private methods + /// <summary> + /// Wait for pending calls to finish + /// </summary> private void WaitForPendingCallsToFinish() { for (; ; ) { if (_pendingCallsCount <= 0) @@ -137,18 +203,28 @@ } } + /// <summary> + /// Adds a pending call + /// </summary> private void AddPendingCall() { #pragma warning disable 0420 Interlocked.Increment(ref _pendingCallsCount); #pragma warning restore 0420 } + /// <summary> + /// Removes a pending call + /// </summary> private void RemovePendingCall() { #pragma warning disable 0420 Interlocked.Decrement(ref _pendingCallsCount); #pragma warning restore 0420 } + /// <summary> + /// Stop hosting envieronment + /// </summary> + /// <param name="immediate">Stop immediately</param> void IRegisteredObject.Stop(bool immediate) { // Unhook the Host so Server will process the requests in the new appdomain. if (_server != null) { @@ -163,36 +239,54 @@ #endregion #region properties + /// <summary> + /// Gets the normalized client script path + /// </summary> public string NormalizedClientScriptPath { get { return _lowerCasedClientScriptPathWithTrailingSlash; } } + /// <summary> + /// Gets the normalized virtual path + /// </summary> public string NormalizedVirtualPath { get { return _lowerCasedVirtualPathWithTrailingSlash; } } + /// <summary> + /// Gets the physical client script path + /// </summary> public string PhysicalClientScriptPath { get { return _physicalClientScriptPath; } } + /// <summary> + /// Gets the physical path + /// </summary> public string PhysicalPath { get { return _configuration.PhysicalPath; } } + /// <summary> + /// Gets the configuration port + /// </summary> public int Port { get { return _configuration.Port; } } + /// <summary> + /// Gets the virtual path + /// </summary> public string VirtualPath { get { return _configuration.VirtualPath; Modified: trunk/Version2/WebServer/Messages.cs =================================================================== --- trunk/Version2/WebServer/Messages.cs 2007-03-09 17:45:37 UTC (rev 33) +++ trunk/Version2/WebServer/Messages.cs 2007-03-09 22:37:29 UTC (rev 34) @@ -16,19 +16,32 @@ namespace MPW.WebServer { - // - // Internal class provides helpers for string formatting of HTTP responses - // + /// <summary> + /// Internal class provides helpers for string formatting of HTTP responses + /// </summary> internal static class Messages { #region variables and constants + /// <summary> + /// HTTP Error format 1 + /// </summary> private const String _httpErrorFormat1 = @"<html> <head> <title>{0}</title> "; + /// <summary> + /// Version String + /// </summary> + public static String VersionString = typeof(Server).Assembly.GetName().Version.ToString(); - public static String VersionString = typeof(Server).Assembly.GetName().Version.ToString(); + /// <summary> + /// Server name + /// </summary> public static String ServerName = typeof(Server).Assembly.GetName().Name; + + /// <summary> + /// HTTP style + /// </summary> private const String _httpStyle = @" <style> body {font-family:""Verdana"";font-weight:normal;font-size: 8pt;color:black;} @@ -43,7 +56,9 @@ .expandable { text-decoration:underline; font-weight:bold; color:navy; cursor:hand; } </style> "; - + /// <summary> + /// HTTP Error format 2 + /// </summary> private static String _httpErrorFormat2 = @" </head> <body bgcolor=""white""> @@ -61,13 +76,18 @@ </body> </html> "; - + /// <summary> + /// Directory listing format 1 + /// </summary> private const String _dirListingFormat1 = @"<html> <head> <title>Directory Listing -- {0}</title> "; + /// <summary> + /// Directory listing format 2 + /// </summary> private const String _dirListingFormat2 = @" </head> <body bgcolor=""white""> @@ -79,6 +99,9 @@ <PRE> "; + /// <summary> + /// Directory listing tail + /// </summary> private static String _dirListingTail = @"</PRE> <hr width=100% size=1 color=silver> @@ -91,15 +114,24 @@ </html> "; + /// <summary> + /// Directory listing parent format + /// </summary> private const String _dirListingParentFormat = @"<A href=""{0}"">[To Parent Directory]</A> "; + /// <summary> + /// Directory listing file format + /// </summary> private const String _dirListingFileFormat = @"{0,38:dddd, MMMM dd, yyyy hh:mm tt} {1,12:n0} <A href=""{2}"">{3}</A> "; + /// <summary> + /// Directory listing directory format + /// </summary> private const String _dirListingDirFormat = @"{0,38:dddd, MMMM dd, yyyy hh:mm tt} <dir> <A href=""{1}/"">{2}</A> "; @@ -107,6 +139,12 @@ #endregion #region public static methods + /// <summary> + /// Formats a body of an error message + /// </summary> + /// <param name="statusCode">Status code</param> + /// <param name="appName">Application name</param> + /// <returns>Formated response body of an error message</returns> public static String FormatErrorMessageBody(int statusCode, String appName) { String desc = HttpWorkerRequest.GetStatusDescription(statusCode); @@ -115,6 +153,13 @@ + String.Format(_httpErrorFormat2, appName, statusCode, desc); } + /// <summary> + /// Formats a body for a directory listing + /// </summary> + /// <param name="dirPath">Path of the directory</param> + /// <param name="parentPath">Parent Path</param> + /// <param name="elements">Elements of the directory</param> + /// <returns></returns> public static String FormatDirectoryListing(String dirPath, String parentPath, FileSystemInfo[] elements) { StringBuilder sb = new StringBuilder(); Modified: trunk/Version2/WebServer/Request.cs =================================================================== --- trunk/Version2/WebServer/Request.cs 2007-03-09 17:45:37 UTC (rev 33) +++ trunk/Version2/WebServer/Request.cs 2007-03-09 22:37:29 UTC (rev 34) @@ -26,12 +26,25 @@ namespace MPW.WebServer { - + /// <summary> + /// Request representation. Handles the request + /// Based on the SimpleWorkerRequest of .NET Framework + /// </summary> internal class Request : SimpleWorkerRequest { #region variables + /// <summary> + /// All bad Path characters + /// </summary> private static char[] badPathChars = new char[] { '%', '>', '<', ':', '\\' }; + + /// <summary> + /// List of default documents + /// </summary> private List<String> _defaultDocuments; + /// <summary> + /// Array of all restricted directory + /// </summary> private static string[] restrictedDirs = new string[] { "/bin", "/app_browsers", @@ -40,62 +53,190 @@ "/app_localresources", "/app_globalresources", "/app_webreferences" }; + + /// <summary> + /// Maximum chunk length + /// </summary> private const int MaxChunkLength = 64 * 1024; + /// <summary> + /// Web server + /// </summary> private Server _server; + + /// <summary> + /// Corresponding host environment + /// </summary> private Host _host; + + /// <summary> + /// Corresponding connection + /// </summary> private Connection _connection; + + /// <summary> + /// Corresponding configuration + /// </summary> private ServerConfiguration _configuration; - // security permission to Assert remoting calls to _connection + /// <summary> + /// security permission to Assert remoting calls to _connection + /// </summary> private IStackWalk _connectionPermission = new PermissionSet(PermissionState.Unrestricted); - // raw request data + /// <summary> + /// raw request data + /// </summary> private const int maxHeaderBytes = 32 * 1024; + + /// <summary> + /// Header bytes + /// </summary> private byte[] _headerBytes; + + /// <summary> + /// Start header offset + /// </summary> private int _startHeadersOffset; + + /// <summary> + /// End header offset + /// </summary> private int _endHeadersOffset; + + /// <summary> + /// Header byte string + /// </summary> private ArrayList _headerByteStrings; - // parsed request data - + /// <summary> + /// Is client script path + /// </summary> private bool _isClientScriptPath; + /// <summary> + /// HTTP method + /// </summary> private string _verb; + + /// <summary> + /// URL + /// </summary> private string _url; + + /// <summary> + /// Protocol + /// </summary> private string _prot; + /// <summary> + /// Path + /// </summary> private string _path; + + /// <summary> + /// File path + /// </summary> private string _filePath; + + /// <summary> + /// Path information + /// </summary> private string _pathInfo; + + /// <summary> + /// Translated Path + /// </summary> private string _pathTranslated; + + /// <summary> + /// Query string + /// </summary> private string _queryString; + + /// <summary> + /// Query string in byte array + /// </summary> private byte[] _queryStringBytes; + /// <summary> + /// Length of content + /// </summary> private int _contentLength; + + /// <summary> + /// Length of content preload + /// </summary> private int _preloadedContentLength; + + /// <summary> + /// Content preload + /// </summary> private byte[] _preloadedContent; + /// <summary> + /// All headers in raw format + /// </summary> private string _allRawHeaders; + + /// <summary> + /// Unknown request headers + /// </summary> private string[][] _unknownRequestHeaders; + + /// <summary> + /// Known request headers + /// </summary> private string[] _knownRequestHeaders; + + /// <summary> + /// + /// </summary> private bool _specialCaseStaticFileHeaders; - // cached response + /// <summary> + /// Indicates, if header is sent + /// </summary> private bool _headersSent; + + /// <summary> + /// Status of the response + /// </summary> private int _responseStatus; + + /// <summary> + /// Response headers + /// </summary> private StringBuilder _responseHeadersBuilder; + + /// <summary> + /// Reponse Body bytes + /// </summary> private ArrayList _responseBodyBytes; + + /// <summary> + /// Remote IP + /// </summary> private String remoteIP; #endregion #region static variables + /// <summary> + /// Int to hex character array + /// </summary> private static char[] IntToHex = new char[16] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; #endregion #region ctor + /// <summary> + /// Constructor + /// </summary> + /// <param name="server">Server</param> + /// <param name="host">Host</param> + /// <param name="connection">Connection</param> + /// <param name="configuration">Server configuration</param> public Request(Server server, Host host, Connection connection,ServerConfiguration configuration) : base(String.Empty, String.Empty, null) { _server = server; @@ -107,6 +248,9 @@ #endregion #region public methods + /// <summary> + /// Process the request + /// </summary> public void Process() { // read the request if (!TryParseRequest()) { @@ -144,7 +288,13 @@ HttpRuntime.ProcessRequest(this); _server.writeInfoAccessLog("Response send by HttpRuntime request for request from: " + remoteIP + " for " + this._path); } + #endregion + #region private methods + /// <summary> + /// Checks for all secuirty options. If access is denied than an error will be sent + /// </summary> + /// <returns>true, if request is allowed; false otherwise</returns> private bool checkSecurity() { if (_configuration.AllowedAccessMode != AccessMode.Global) { if (_configuration.AllowedAccessMode == AccessMode.Local) { @@ -165,7 +315,7 @@ } } if (!ipFound) { - _connection.WriteErrorAndClose(403); + _connection.WriteErrorAndClose(403); _server.writeErrorAccessLog("Send error 403 (Acces Denied by configuration) for request from: " + remoteIP + " for " + this._path); return false; } @@ -192,9 +342,13 @@ } return true; } - #endregion - - #region private methods + + /// <summary> + /// Sends a response from a file stream + /// </summary> + /// <param name="f">Filestream</param> + /// <param name="offset">Offset</param> + /// <param name="length">Length</param> private void SendResponseFromFileStream(FileStream f, long offset, long length) { long fileSize = f.Length; @@ -233,6 +387,9 @@ } } + /// <summary> + /// Reset the request + /// </summary> private void Reset() { _headerBytes = null; _startHeadersOffset = 0; @@ -262,6 +419,10 @@ _specialCaseStaticFileHeaders = false; } + /// <summary> + /// Tries to parse the request. If request couldn't be parsed than an error will be sent + /// </summary> + /// <returns>true, if request can be parsed;false otherwise</returns> private bool TryParseRequest() { Reset(); @@ -302,6 +463,10 @@ return true; } + /// <summary> + /// Tries to read all headers from the request + /// </summary> + /// <returns>true, if everything can be parsed; false otherwise</returns> private bool TryReadAllHeaders() { // read the first packet (up to 32K) byte[] headerBytes = _connection.ReadRequestBytes(maxHeaderBytes); @@ -353,6 +518,9 @@ return true; } + /// <summary> + /// Reads all request headers + /// </summary> private void ReadAllHeaders() { _headerBytes = null; @@ -365,6 +533,9 @@ while (_endHeadersOffset < 0); // found \r\n\r\n } + /// <summary> + /// Parse a request line + /// </summary> private void ParseRequestLine() { ByteString requestLine = (ByteString)_headerByteStrings[0]; ByteString[] elems = requestLine.Split(' '); @@ -433,6 +604,10 @@ _pathTranslated = MapPath(_filePath); } + /// <summary> + /// Checks if the path is not allowed + /// </summary> + /// <returns>true, if it is a bad path; false otherwise</returns> private bool IsBadPath() { if (_path.IndexOfAny(badPathChars) >= 0) { return true; @@ -449,6 +624,9 @@ return false; } + /// <summary> + /// Parse the request headers + /// </summary> private void ParseHeaders() { _knownRequestHeaders = new string[RequestHeaderMaximum]; @@ -496,6 +674,9 @@ } } + /// <summary> + /// Parse posted content in the request + /// </summary> private void ParsePostedContent() { _contentLength = 0; _preloadedContentLength = 0; @@ -522,6 +703,9 @@ } } + /// <summary> + /// Skips all posted content + /// </summary> private void SkipAllPostedContent() { if (_contentLength > 0 && _preloadedContentLength < _contentLength) { int bytesRemaining = (_contentLength - _preloadedContentLength); @@ -536,6 +720,10 @@ } } + /// <summary> + /// Checks if this is a request for a restricted directory + /// </summary> + /// <returns>true, if it is a request for a restricted directory; false otherwise</returns> private bool IsRequestForRestrictedDirectory() { String p = CultureInfo.InvariantCulture.TextInfo.ToLower(_path); @@ -554,6 +742,10 @@ return false; } + /// <summary> + /// Process a directory listing request + /// </summary> + /// <returns>true, if request could be handled; false otherwise</returns> private bool ProcessDirectoryListingRequest() { if (_verb != "GET") { return false; @@ -626,6 +818,9 @@ return true; } + /// <summary> + /// Prepares the response + /// </summary> private void PrepareResponse() { _headersSent = false; _responseStatus = 200; @@ -636,6 +831,11 @@ #endregion #region private static methods + /// <summary> + /// Encodes a url redirect + /// </summary> + /// <param name="path">Path</param> + /// <returns>encoded url redirect</returns> private string UrlEncodeRedirect(string path) { // this method mimics the logic in HttpResponse.Redirect (which relies on internal methods) try { @@ -682,6 +882,10 @@ #endregion #region Implementation of HttpWorkerRequest + /// <summary> + /// Flushs the response + /// </summary> + /// <param name="finalFlush">Do a final flush? (Close connection)</param> public override void FlushResponse(bool finalFlush) { _connectionPermission.Assert(); @@ -702,6 +906,9 @@ } } + /// <summary> + /// Handles end of request + /// </summary> public override void EndOfRequest() { Connection conn = _connection; @@ -710,44 +917,85 @@ _connection = null; } } + + /// <summary> + /// Gets uri path + /// </summary> + /// <returns>Uri path</returns> public override string GetUriPath() { return _path; } + /// <summary> + /// Gets query string + /// </summary> + /// <returns>Query string</returns> public override string GetQueryString() { return _queryString; } + /// <summary> + /// Gets query string as raw bytes + /// </summary> + /// <returns>query string as raw bytes</returns> public override byte[] GetQueryStringRawBytes() { return _queryStringBytes; } + /// <summary> + /// Gets raw url + /// </summary> + /// <returns>Raw url</returns> public override string GetRawUrl() { return _url; } + /// <summary> + /// Gets http method + /// </summary> + /// <returns>Http method</returns> public override string GetHttpVerbName() { return _verb; } + /// <summary> + /// Gets http protocol + /// </summary> + /// <returns>Http protocol</returns> public override string GetHttpVersion() { return _prot; } + /// <summary> + /// Gets remote IP/address + /// </summary> + /// <returns>Remote IP/address</returns> public override string GetRemoteAddress() { _connectionPermission.Assert(); return _connection.RemoteIP; } + /// <summary> + /// Gets remote port + /// </summary> + /// <returns>Remote port</returns> public override int GetRemotePort() { return 0; } + /// <summary> + /// Gets local address + /// </summary> + /// <returns>Local address</returns> public override string GetLocalAddress() { _connectionPermission.Assert(); return _connection.LocalIP; } + /// <summary> + /// Gets server name + /// </summary> + /// <returns>Server name</returns> public override string GetServerName() { string localAddress = GetLocalAddress(); if (localAddress.Equals("127.0.0.1")) { @@ -756,38 +1004,76 @@ return localAddress; } + /// <summary> + /// Gets local port + /// </summary> + /// <returns>Local port</returns> public override int GetLocalPort() { return _host.Port; } + /// <summary> + /// Gets file path + /// </summary> + /// <returns>File path</returns> public override string GetFilePath() { return _filePath; } + /// <summary> + /// Gets translated file path + /// </summary> + /// <returns>Translated file path</returns> public override string GetFilePathTranslated() { return _pathTranslated; } + /// <summary> + /// Gets Path information + /// </summary> + /// <returns>Path infroamtion</returns> public override string GetPathInfo() { return _pathInfo; } + /// <summary> + /// Gets application path + /// </summary> + /// <returns>Application Path</returns> public override string GetAppPath() { return _host.VirtualPath; } + /// <summary> + /// Gets translated application path + /// </summary> + /// <returns>Translated application path</returns> public override string GetAppPathTranslated() { return _host.PhysicalPath; } + /// <summary> + /// Gets preload entity body + /// </summary> + /// <returns>Preload entity body</returns> public override byte[] GetPreloadedEntityBody() { return _preloadedContent; } + /// <summary> + /// Returns a value indicating whether all request data is available and no further reads from the client are required + /// </summary> + /// <returns>true, if all request data is available; false otherwise</returns> public override bool IsEntireEntityBodyIsPreloaded() { return (_contentLength == _preloadedContentLength); } + /// <summary> + /// Reads entity body + /// </summary> + /// <param name="buffer">Buffer</param> + /// <param name="size">Size of buffer</param> + /// <returns>Number of read bytes</returns> public override int ReadEntityBody(byte[] buffer, int size) { int bytesRead = 0; @@ -802,10 +1088,20 @@ return bytesRead; } + /// <summary> + /// Gets known request headers with given index + /// </summary> + /// <param name="index">Index of the header</param> + /// <returns>Request header</returns> public override string GetKnownRequestHeader(int index) { return _knownRequestHeaders[index]; } + /// <summary> + /// Gets unknown request header with the given name + /// </summary> + /// <param name="name">Name of the header</param> + /// <returns>Request header</returns> public override string GetUnknownRequestHeader(string name) { int n = _unknownRequestHeaders.Length; @@ -818,10 +1114,19 @@ return null; } + /// <summary> + /// Gets all unknown request headers + /// </summary> + /// <returns>All unknown request headers</returns> public override string[][] GetUnknownRequestHeaders() { return _unknownRequestHeaders; } + /// <summary> + /// Gets server variable + /// </summary> + /// <param name="name">Name of the server variable</param> + /// <returns>Value of the server variable</returns> public override string GetServerVariable(string name) { string s = String.Empty; @@ -842,6 +1147,11 @@ return s; } + /// <summary> + /// Maps a path from virutal to physical + /// </summary> + /// <param name="path">Virtual path</param> + /// <returns>Physical path</returns> public override string MapPath(string path) { string mappedPath = String.Empty; bool isClientScriptPath = false; @@ -883,10 +1193,20 @@ return mappedPath; } + /// <summary> + /// Sends status + /// </summary> + /// <param name="statusCode">Statuscode</param> + /// <param name="statusDescription">Descritption</param> public override void SendStatus(int statusCode, string statusDescription) { _responseStatus = statusCode; } + /// <summary> + /// Sends known response header + /// </summary> + /// <param name="index">Index of the response header</param> + /// <param name="value">Value of the response header</param> public override void SendKnownResponseHeader(int index, string value) { if (_headersSent) { return; @@ -921,6 +1241,11 @@ _responseHeadersBuilder.Append("\r\n"); } + /// <summary> + /// Sends unknown response header + /// </summary> + /// <param name="name">Name of the response header</param> + /// <param name="value">Value of the response header</param> public override void SendUnknownResponseHeader(string name, string value) { if (_headersSent) return; @@ -931,6 +1256,10 @@ _responseHeadersBuilder.Append("\r\n"); } + /// <summary> + /// Sends calculated content length + /// </summary> + /// <param name="contentLength">Content Length</param> public override void SendCalculatedContentLength(int contentLength) { if (!_headersSent) { _responseHeadersBuilder.Append("Content-Length: "); @@ -939,20 +1268,36 @@ } } + /// <summary> + /// Returns if the headers are sent + /// </summary> + /// <returns>true, if the headers are sent;false otherwise</returns> public override bool HeadersSent() { return _headersSent; } + /// <summary> + /// Checks, if the client is connected + /// </summary> + /// <returns>true, if client is connected; false otherwise</returns> public override bool IsClientConnected() { _connectionPermission.Assert(); return _connection.Connected; } + /// <summary> + /// Closes the connection + /// </summary> public override void CloseConnection() { _connectionPermission.Assert(); _connection.Close(); } + /// <summary> + /// Sends response from memory + /// </summary> + /// <param name="data">Data to send</param> + /// <param name="length">Length of the data</param> public override void SendResponseFromMemory(byte[] data, int length) { try { if (length > 0) { @@ -966,6 +1311,12 @@ } } + /// <summary> + /// Sends a response from file + /// </summary> + /// <param name="filename">Filename</param> + /// <param name="offset">Offset</param> + /// <param name="length">Length</param> public override void SendResponseFromFile(string filename, long offset, long length) { if (length == 0) { return; @@ -984,6 +1335,12 @@ } } + /// <summary> + /// Sends a response from file + /// </summary> + /// <param name="handle">Handle of the file</param> + /// <param name="offset">Offset</param> + /// <param name="length">Length</param> public override void SendResponseFromFile(IntPtr handle, long offset, long length) { if (length == 0) { return; Modified: trunk/Version2/WebServer/Server.cs =================================================================== --- trunk/Version2/WebServer/Server.cs 2007-03-09 17:45:37 UTC (rev 33) +++ trunk/Version2/WebServer/Server.cs 2007-03-09 22:37:29 UTC (rev 34) @@ -21,27 +21,67 @@ using log4net; namespace MPW.WebServer { - + /// <summary> + /// Main class of the web server + /// </summary> public sealed class Server : MarshalByRefObject { #region variables + /// <summary> + /// Global web server log + /// </summary> private ILog log = LogManager.GetLogger("WebServer"); + + /// <summary> + /// Log of all access + /// </summary> private ILog accessLog = LogManager.GetLogger("WebServerAccess"); + + /// <summary> + /// Configuration of the server + /// </summary> private ServerConfiguration _configuration; + /// <summary> + /// Callback on start + /// </summary> private WaitCallback _onStart; + + /// <summary> + /// Callback on socket accept + /// </summary> private WaitCallback _onSocketAccept; + /// <summary> + /// Indicates, if shutdown is in progress + /// </summary> private bool _shutdownInProgress; + /// <summary> + /// Indicates, if server is pause + /// </summary> private bool _pause; + /// <summary> + /// Application Manager + /// </summary> private ApplicationManager _appManager; + /// <summary> + /// Server socket + /// </summary> private Socket _socket; + + /// <summary> + /// Hosting environment + /// </summary> private Host _host; #endregion #region ctor + /// <summary> + /// Constructor + /// </summary> + /// <param name="configuration">Server configuration</param> public Server(ServerConfiguration configuration) { _configuration = configuration; String tempPhysicalPath = configuration.PhysicalPath; @@ -56,6 +96,10 @@ #endregion #region overrides + /// <summary> + /// Never expire the license + /// </summary> + /// <returns>Object</returns> public override object InitializeLifetimeService() { // never expire the license return null; @@ -63,24 +107,36 @@ #endregion #region prroperties + /// <summary> + /// Gets the virtual Path + /// </summary> public string VirtualPath { get { return _configuration.VirtualPath; } } + /// <summary> + /// Gets the physical path + /// </summary> public string PhysicalPath { get { return _configuration.PhysicalPath; } } + /// <summary> + /// Gets the port + /// </summary> public int Port { get { return _configuration.Port; } } + /// <summary> + /// Gets the root url + /// </summary> public string RootUrl { get { if (_configuration.Port != 80) { @@ -91,10 +147,17 @@ } } + /// <summary> + /// Gets/Sets if the server is paused + /// </summary> public bool Pause { get { return _pause; } set { _pause = value; } } + + /// <summary> + /// Gets the bind address + /// </summary> public String BindAdress { get { return _configuration.BindAddress; @@ -103,9 +166,9 @@ #endregion #region public methods - // - // Socket listening - // + /// <summary> + /// Starts the server and listen on the socket + /// </summary> public void Start() { _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _socket.ExclusiveAddressUse = true; @@ -124,6 +187,9 @@ log.Debug("Web server started. Listening on: " + _configuration.BindAddress + ":" + _configuration.Port); } + /// <summary> + /// Stops the server, all connections and the hosting environment + /// </summary> public void Stop() { _shutdownInProgress = true; @@ -152,6 +218,11 @@ _shutdownInProgress = false; } } + + /// <summary> + /// Reloads the web server with a new configuration + /// </summary> + /// <param name="configuration">New configuration</param> public void Reload(ServerConfiguration configuration) { Stop(); _configuration = configuration; @@ -162,6 +233,10 @@ #endregion #region event handling + /// <summary> + /// Handles on socket accept event and process the new request + /// </summary> + /// <param name="acceptedSocket">New socket</param> private void OnSocketAccept(object acceptedSocket) { if (!_shutdownInProgress) { Connection conn = new Connection(this, (Socket)acceptedSocket, _configuration); @@ -192,14 +267,21 @@ } } - // called at the end of request processing - // to disconnect the remoting proxy for Connection object - // and allow GC to pick it up + /// <summary> + /// called at the end of request processing + /// to disconnect the remoting proxy for Connection object + /// and allow GC to pick it up + /// </summary> + /// <param name="conn">Corresponding connection</param> internal void OnRequestEnd(Connection conn) { RemotingServices.Disconnect(conn); //accessLog.Info("Request from: " + conn.RemoteIP + " done."); } + /// <summary> + /// Handles the on start event and listens on the socket + /// </summary> + /// <param name="unused"></param> private void OnStart(Object unused) { while (!_shutdownInProgress) { try { @@ -213,6 +295,10 @@ #endregion #region private methods + /// <summary> + /// Generates the hosting environment + /// </summary> + /// <returns>Hosting environment, if possible</returns> private Host GetHost() { if (_shutdownInProgress) return null; @@ -242,15 +328,34 @@ #endregion #region internal methods + /// <summary> + /// Called, when host is stopped. Sets internal variable to null + /// </summary> internal void HostStopped() { _host = null; } + + /// <summary> + /// Writes an error to log + /// </summary> + /// <param name="message">Error message</param> + /// <param name="e">Corresponding Exception</param> internal void WriteErrorToLog(String message, Exception e) { log.Error(message, e); } + + /// <summary> + /// Writes an access information to the access log + /// </summary> + ... [truncated message content] |