Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

A Micro Web Server Using WCF

4.84/5 (11 votes)
7 Jul 2014Apache8 min read 25.1K   1K  
Using Windows Communication Foundation to build a simple web server

Introduction

This project allows you to create and maintain a low-usage server without the overhead of Apache or IIS.

Background

I was tasked to provide a communications server which would send notifications to clients about events on the server. Part of that task was to allow the communications server to be configured through a website. There was a restriction to this service where we were not allowed to install anything else but our software which meant that we could not use IIS as the host. I finished writing the server as self-hosted NT service and I wrote a proof of concept website in the same host that could serve up an HTML page and an image on that page. The project stalled and the proof of concept was dead.

This is my effort to take that proof of concept and show that WCF can be used to implement a low usage, administrative website.

Requirements

This project was built with Visual Studio 2013 and .NET 4.5, however, I have also included solutions for VS2010 and VS2012 and they both use .NET 4.0. From what I understand, VS2008 does not support .NET 4.0, so I have not created a solution for that and I am not planning to create one.

You must run DBWebHost.exe as an administrator. The explanation is below. You will also need to open up the appropriate ports on your firewall to allow other clients to access your website.

The Components

There are five components to this project:

  • DBWebHost.exe
  • HttpAccess.dll
  • DBServerLibrary.dll
  • DBWebsite.dll
  • DBTestsite.dll

DBWebHost is the main program. It is responsible for creating a WebServiceHost, opening it, closing it and cleaning up. I am using WebServiceHost instead of ServiceHost because it is simpler to set up programmatically and does not require behaviors to be explicitly configured.

To enable HTTP access to your service, you will have to open the port you want to use on your firewall and you will need to configure HTTP.SYS to allow access. Most sites show you how to configure HTTP.SYS through the command shell like so:

C++
netsh http add urlacl http://+:80/[service_name] user=[DOMAIN\user_name] sddl=[DACL] 

HttpAccess.dll, however, uses HttpSetServiceConfiguration which is part of the HttpApi library. To use this method with the DACL specified, you need to run this as an administrator. I really don't recommend this approach. A better way would be to set this during installation which I have done in the past. I have not done it for this project because I wanted to show how HttpSetServiceConfiguration works. If you were to call HttpAccess.dll from an installer or separate process, DBWebHost.exe could run without admin privileges.

DBServerLibrary.dll is the router. The IDBWebService interface defines the routing structure and the parameters accepted by the service. DBWebService implements the interface. I have rewritten this considerably since the last release. The interface now only supports Get and Post methods. There are stubs for Put and Delete which are not active, though I may be implementing these later.

DBWebService will take the incoming request and parse it into the host name, the website name, the controller name and the action name. Anything after the action name is treated as part of the query string. It will then take this information along with any streams that are passed and create a Request object and that request object may be passed to the Action method.

There are two ways of defining your Controller class and Action method.

The first is the way previous versions defined them: URIs for pages are constructed with /dbwebservice/{website}/{controller}/{action} where {website} is the name of the website module you are hosting, {controller} is the prefix name of the Controller class and {action} is the name of the method to call on the Controller. These values are case insensitive. DBWebService will take these values, set them to lower case, and then set the first letter to upper case. For instance, the in path /dbwebservice/HOME/index, DBWebService will convert "HOME" to "Home" and append "Controller" so that the name of the expected Controller is "HomeController". It will take "index" and convert it to "Index" so that it can look for the "Index" method on the "HomeController" object.

The second way is determined by attributes on the Controller class and Action method. I have included a PathResolutionAttribute class which can be used to decorate the Controller classes and Action methods. A helper class in DBServerLibrary.dll called ParsedUri will take the {controller} and {action} values and look for matches in the website assembly. If it fails to find those values in the assembly, it will fall back to the old method of finding the Controller class and Action method.

Now, I should mention that the UriTemplate properties on the WebGet and WebInvoke attributes have been changed from /{controller}/{action} to /{*path}. This allows the request string to be virtually anything so it is up to the server to parse the request rather than relying on the underlying dispatch system.

I have included the interfaces IErrorController and IResourceController which are special case Controllers and are used to display errors and return resource files such as images, JavaScript, CSS files, etc. DBWebService used to handle resources directly, but since it can now handle multiple websites, resource and error requests should now be handled by the site itself. DBWebService looks for these interfaces when it cannot resolve request string and tries to resolve it first with the IResourceController and then with the IErrorController. If the request string cannot be resolved through either interface, a default ErrorController object will be created and returned.

An added benefit to all of this is that the HTML links you use on your websites can now be relative links. This was not supported in earlier versions.

DBWebsite.dll and DBTestsite.dll are the implementations of websites which render the HTML and provide the assets (images scripts, etc.) for the websites and they are used to demonstrate how Controllers and Actions work. They demonstrate how CSS is applied, that they support JavaScript and JavaScript libraries such as jQuery, Ajax GETs and Json posts, as well as support for CGI processing through PHP. As of this release, you are no longer bound to only one website for your host.

Configuration

There are three values that can be configured in the App.config (actually it is DBWebHost.exe.config):

  • port is the value of the port that will be opened for request. The default is 80. It is used by DBWebHost.
  • secureport is the value of the port that will be opened for SSL requests. It is used by DBWebHost.
  • cert is the name (subject) of the certificate you are using for SSL requests. It is used by DBWebHost.

There is a section called ProcessorSection which contains the information about the different CGI processors you can use. You are no longer restricted to one type of processor. This replaces cgiExec, cgiType, and cgiParams.

There is another section called SitesSection which contains the name of the website modules you want to include and their paths. If you have a problem with loading a website assembly, this is the first place to look. This replaces the module configuration value.

Points of Interest

Be careful with editing PHP files in Visual Studio. PHP does not like Byte Order Marks (BOM) in the files. See PhpProcessor.cs for details on how to save files used by the PHP processor.

To test SSL, you will need to either have purchased a certificate or create one from your own personal certificate authority. I recommend reading How To Be Your Own Certificate Authority and Create Your Own Certificate to Sign Code Files by Mike Meinz. The server is expecting the certificate to be located in the "My" store in the LocalMachine store location. I will be making this configurable in future releases.

If you are having any trouble loading the website assemblies, the first place to check is the DBWebHost App.Config file and make sure that the path values in the SitesSection area are correct.

Check the release notes for fixes and additions.

There is a TODO.txt file which outlines future improvements. If you have a request for a feature which is not in the TODO list, let me know.

The binaries included are built using VS2013.

History

  • Release 1.0: 5/5/2014
    • First release
  • Release 1.0.2: 5/18/2014
    • Added support for Visual Studio 2010 and 2012
    • Added support for Perl processing
    • Restructured Session collection so sessions can be cleaned up on a worker thread
    • Changed JQuery method to an overloaded JavaScript method so any library can be used
    • Fixed various bugs
  • Release 1.0.3: 6/17/2014
    • Added support for SSL
    • Added support for custom error pages
    • Added support for multiple processor engines
    • Added support for multi-part form data (used for uploading files)
    • Added support for redirection to a default page on a bad URI
  • Release 1.1.1: 7/6/2014
    • Added support for multiple websites
    • Simplified service interface
    • Deferred resource requests to websites
    • Allow for relative paths of links and resources in HTML
    • Allow definition of Controller and Action names by attributes

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0