Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Explore and Extend the Cassini Web Server : Page 2

Extend the power of Cassini, Microsoft's source-available Web server based on the .NET Framework, to provide a lightweight HTTP server for your applications.

Cassini Under the Hood
The Cassini Web server consists of a number of modules that together support the Web hosting functions. You'll find the main functions that you'll need to implement Cassini in your own application in the Cassini.Server class. The Server class exposes methods that let you start and stop the Web server. Instances of the Server object are created in the caller's App Domain. The Server creates and configures the new App Domain to process HTTP requests with the System.Web.Hosting.ApplicationHost.CreateApplicationHost() method. The Server class also exposes properties that enable you to specify the physical path for the server files, the port number to be used for HTTP, and the root URL to identify the server. The Server class exposes additional public methods such as Start(), Stop(), and Restart()to support Server application management. By using the Start() method of the Server class constructor, you can implement a private member named CreateHost() as shown below:

private void CreateHost() { _host = (Host)ApplicationHost.CreateApplicationHost (typeof(Host), _virtualPath, _physicalPath); _host.Configure(this, _port, _virtualPath, _physicalPath, _installPath); }

The preceding method implements Cassini.Host and its Configure() method to configure a server process. This method implements a delegate called _onSocketAccept, which spawns a thread to process a new socket connection using the private member OnSocketAccept(). This method establishes a connection using Cassini.Connection and processes the request.

Cassini is missing a key feature—logging. As delivered, the server maintains no logs for the incoming requests. But logging is a useful feature for monitoring the use of certain functions in an application, or the times of day that an application experiences the greatest load. This opens up the possibility of extending Cassini to implement this feature, leveraging the availability of the source and an understanding of the internal workings to create log entries when the server accepts new connections and initiates request processing.

Extending Cassini Functionality
To implement a logging feature in Cassini, first determine what type of activity you want to capture. To make it simple, you can start by logging every socket connection that is established and processed. To do that, you can add logging functionality to the OnSocketAccept() function referenced earlier. You'll use the Connection object's ProcessOneRequest() method, which in turn implements the Request object's Process() method.

To add functionality to Cassini, create a module containing the new classes and then implement those classes in the Cassini source. Review the sample logging class in Listing 1. This class exposes a public method called CreateLogEntry(). This method implements uses a StreamWriter object to create a log file if one does not exist and then uses the StreamWriter.WriteLine() method to write a string value to the log file. Finally, it closes the StreamWriter. You can find the class in the module named CassiniLogger.cs in the sample code. To implement this functionality in Cassini, you'll need to modify the Cassini source and modify the build script used to create the Cassini assemblies.

To log an entry for each incoming socket connection as described, change the OnSocketAccept method of the Host class as shown below:

private void OnSocketAccept(Object acceptedSocket) { Connection conn = new Connection(this, (Socket)acceptedSocket); //***** Modification to Cassini source code ***** //Logging extension to track connections //Use CassiniLogger class to write connection //class data to log file CassiniLogger cl = new CassiniLogger( "c:\\Cassini\\Logs\\CustomLog.txt"); //Create log entry to track remote IP for //connection and indicator //of a local or remote connection cl.CreateLogEntry(conn.RemoteIP + ", " + conn.IsLocal); //***** End modification ***** conn.ProcessOneRequest(); }

As you can see, the code alterations instantiate an instance of the CassiniLogger class using the constructor to specify the location for the log file. The code then calls the CreateLogEntry() method concatenates the RemoteIP and IsLocal properties of the Connection object into a string passed as the input parameter. In other words, the additional code captures the requesting IP address and the value of the IsLocal property from the instance of the Connection object which the server creates when the OnSocketAccept() method executes. The code logs an entry every time the Cassini server receives a request to process.

Running Cassini with Extensions
Now that you've modified the code to support the new extensions you need to compile a new version of the application that includes the updates. For more on debugging the changes you've made to the Cassini source, see the section of this article titled Debugging Cassini. The Cassini source download includes a build script that uses the command line C# compiler (csc.exe) to build the source; but to add the new functionality to the application, you need to build it using the modified Host.cs class and the new CassiniLogger.cs module. To do that, copy the CassiniLogger.cs file to the Cassini directory and modify the build script, a file named build.bat, as follows:

csc /t:library /r:System.dll /r:System.Web.dll /debug /out:Cassini.dll AssemblyInfo.cs ByteParser.cs ByteString.cs Connection.cs Host.cs Messages.cs Request.cs Server.cs CassiniLogger.cs

Modifying the build script adds the CassiniLogger.cs module to the build, and also uses the /debug switch to output debugging information that may be useful when you need to step through these changes to the code. Executing the modified build script creates the CassiniWebServer.exe application and builds and registers the Cassini type library in the Global Assembly Cache.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.