CHAPTER 2
The source code presented in this chapter is in the folder Examples\Chapter 2\Demo in the Bitbucket repository.
Writing a web server is essentially rather simple. If all we wanted to do is serve up some HTML pages, we could be done with the following implementation.
Namespaces we need to use:
using System; |
Code Listing 1
A couple helpful extension methods:
/// <summary> /// Some useful string extensions. /// </summary> public static class ExtensionMethods { /// <summary> /// Return everything to the left of the first occurrence of the specified string, /// or the entire source string. /// </summary> public static string LeftOf(this String src, string s) { string ret = src; int idx = src.IndexOf(s); if (idx != -1) { ret = src.Substring(0, idx); } return ret; } /// <summary> /// Return everything to the right of the first occurrence of the specified string, /// or an empty string. /// </summary> public static string RightOf(this String src, string s) { string ret = String.Empty; int idx = src.IndexOf(s); if (idx != -1) { ret = src.Substring(idx + s.Length); } return ret; } |
Code Listing 2
And the program itself:
Code Listing 3
The previous code initializes 20 listeners. Using semaphores, when a request is received, the semaphore is released and a new listener is created. This code can therefore receive 20 requests simultaneously. We rely on the await mechanism to determine on what thread the continuation (the code after the await) executes. If you are unfamiliar with the use of Task and async/await, Stephan Cleary has an excellent discussion of async/await and execution contexts on his blog at http://blog.stephencleary.com/2012/02/async-and-await.html.
There are two more things we need to do.
First, create an index.html file with the contents:
<p>Hello World</p> |
Code Listing 4
The server we just wrote will run within the bin\Debug folder (assuming you haven’t changed the build configuration from “Debug” to “Release”), so we need to put the index.html file into the bin\Debug folder so the application can find it when it tries to load the page associated with the URL.

Figure 2: Solution Tree
Second, put an icon file named favicon.ico into the bin\Debug folder as well; otherwise, if the browser requests it, the web server will throw a File Not Found exception.
Now, when you run the console app, it will wait for a connection. Fire up your browser and for the URL, and enter:
http://localhost/index.html |
Code Listing 5
I am assuming here that you do not have a server already running on port 80 on your machine—if you do, the program will fail.
In the console window you'll see the path emitted, and in the browser you'll see the page rendered as shown in the following figure.

Figure 3: Serving Static Content
Issues with localhost?
If your browser is having problems connecting to localhost, edit your C:\Windows\System32\drivers\etc\hosts file and make sure there is an entry that looks like this:
127.0.0.1 localhost
If it's missing, add it, save the file, and reboot the computer.
We created a simple server that serves only a static HTML page, but there are many things wrong with it:
Request routing combined with some sort of a controller implementation is really useful when implementing a REST[26] API, something our web server should be able to do as well. REST is also at the center of AJAX[27] and AJAJ[28] requests (SOAP[29] is another common protocol, but REST is much more in vogue nowadays), allowing us to write single-page applications. Here we are implicitly entering into the realm of serving dynamic content. If you're rendering mostly static content, then you could also look at Apache (especially in conjunction with PHP) or Nginx, both of which are primarily static content web servers, but with support for dynamic content.[30]
If you look at a few popular middleware frameworks, such as ASP.NET,[31] Ruby on Rails,[32] or NancyFx[33] (which can run standalone as a server or under IIS as middleware), you'll immediately get a sense that there is a sophisticated architecture supporting the web server. There’s also some very clever built-in functionality that doesn't have anything to do with handling requests, but tends to make the job easier because there's a typical set of common tasks people need to perform when creating a professional website.
If you use any of these frameworks, you will almost immediately notice one or more of the following characteristics:
Underlying these three common features of popular web servers and middleware are three very important premises:
Note that in the web server implementation presented in this book, the MVC pattern is not baked into the architecture—you are free to use an MVC pattern or not for handling web requests.
The trend (especially as "push servers;” see SignalR[39]) is to move toward single-page applications (SPAs)—the content of the page updates without requiring a full page refresh. A full page refresh requires a callback to the server to load all the content, whereas an SPA requests only the content that it needs.
This makes developing a web application more complicated because you’re not just rendering the page on the server—you’re coding in JavaScript on the client-side to implement the dynamic behavior, and probably using additional JavaScript packages such as jQuery,[40] Knockout,[41] Backbone,[42] Angular,[43] or any number of available options. Furthermore, you’re not just writing “render this page” server-side and client-side code. Instead, a significant portion of what you write on the server will look more like an API to support AJAX/REST callbacks to return the content the client is requesting. In fact, it probably is helpful to think more in terms of writing an API than in terms of writing a website!
The simple answer is: no.
The whole reason I have even bothered to write yet another web server from scratch is because those features, which are often integrated with the basic process of a web server and initialized in a new project template, are, while not altogether unnecessary, sometimes better served by a lightweight version of the feature.
The question often comes up of whether to build your own or buy into an existing architecture, and the deeper question, why are we always rewriting prior work?
The answer to both, and the premise of why you're reading this book (other than to learn about the internals of how web servers work) is that, based on the experiences of working with other technologies, you have discovered that your needs are not being met by the existing solutions.
The typical answer, "because the existing technology can be improved upon," is actually a weak argument, especially when one considers that any new technology will have deficiencies in areas other than the technology that it replaces. So, my motivations are to write a web server that not only meets his or her needs but also employs an architecture that does not hinder you from meeting your needs. The premise of such architecture is that the function of a web server should be completely decoupled from paradigms such as MVC, as well as view engine and ORM implementations. These should be in the purview of, if not the application, then at least some middle-tier that you can take or leave depending on your needs.