Tag Archives: Server

It serves!

Writing a Working Web Server in Under 100 Lines of Code

UPDATE 12/30/12: You must run the compiled executable as an administrator on systems with UAC. Otherwise, the program will crash and exit.

I was reading some coding blogs last night and I happened across a guy who had written a minuscule web server in C# over his lunch break. I knew right away that I had to have one, too. A server like this one serves little practical purpose; it does not support any sort of server-side scripting (though adding a BASIC interpreter to it would be extremely cool). It does, however, serve a wide variety of documents (and will detect their MIME type prior to serving them.

The first thing I did was write a Log method to make console output look fancy:

static void Log(string message)
    Console.WriteLine("[{0}]: {1}", DateTime.Now, message);

That was quick. Next was to create an actual server method, which would continuously loop and be started asynchronously by the Main() method.

static void RunServer(int port)
    HttpListener listener = new HttpListener();
    listener.Prefixes.Add(string.Format("http://+:{0}/", port)); // Where to listen
    listener.Start(); // Fire up the server
    while (true)
        HttpListenerContext context = listener.GetContext(); // Get a connection
        Log("Requested: " + context.Request.Url);
        if (context.Request.RawUrl.EndsWith("/")) // Just asking for index; redirect
            context.Response.StatusCode = 301;
            context.Response.StatusDescription = "301 Moved Permanently";
            context.Response.RedirectLocation = context.Request.RawUrl + "index.html"; // Just send browser to index.html
            Log("Redirected client to /index.html.");
            byte[] retVal;
                retVal = File.ReadAllBytes("www" + context.Request.RawUrl); // Give the client the requested page
                context.Response.StatusCode = 404; // File not found; tell the client.
                context.Response.StatusDescription = "404 Not Found";
                Log("404: " + context.Request.RawUrl);
            string mime = GetMimeType(context.Request.RawUrl);
            context.Response.Headers.Add(HttpResponseHeader.ContentType, mime); // Give response a MIME type
            Stream respStream = context.Response.OutputStream; // Response stream we'll write to
            respStream.Write(retVal, 0, retVal.Length);
            context.Response.StatusCode = 200;
            context.Response.StatusDescription = "200 OK"; // Let client know stuff is okay
            Log("Served: " + context.Request.RawUrl + " as " + mime + ".");

There’s quite a bit going on here; I’ll try to explain it all. The beginning of this method configures an HttpListener to wait for connections and starts it. It then starts an infinite loop (the user can stop the server by closing the console window) of continuously waiting for and serving requests (listener.GetContext() blocks until something makes a request). A client can ask for three things in this simple server (since we’re not running server-side scripts):

  1. A page that exists (e.g. /index.html)
  2. A page that doesn’t exist (e.g. __++++_____-sdasfash1234.abcdf)
  3. A directory root that may or may not exist (e.g. /mysite/)

The first thing we check for is the directory root case. We check the URL to see if it ends in a slash. If it does, we redirect the client to whatever the path is, plus “index.html.” We could simply serve the “index.html” off the bat, but we’re (poorly) code golfing right now! That would take more code and accomplish essentially the same thing!

If the RawUrl does not end in a slash, we’re serving a page! Anyway, the server stores all its files in a “www” folder that lives in the same directory as the executable. Fetching “www” plus the raw path with File.ReadAllBytes gets the data of the file we are looking for. The try statement is used in the event we can’t find the file. If an exception does get raised, the server returns a “404 File Not Found” error to the client. If this server were fancy, it would also serve a 404 error page.

If the server can find the file, it determines the MIME type through a simplistic process I will detail in a moment, sets the MIME type in the header, writes the data to the client stream, gives the client a “200 OK” (I am not sure if the order matters for all this, or if all the data simply gets flushed when the HttpResponse gets closed, but this code worked fine in Chrome and IE 9), and closes the connection.

The last thing is to determine the MIME type of the file. Rather than using WIN32 interop calls, I decided to write a simplistic method of my own based on the file extension being served.

static string GetMimeType(string fileName)
    // Fast way of determining if the lowercase file name ends with something
    Func<string, bool> fendw = new Func<string, bool>(inp => fileName.ToLower().EndsWith(inp));
    // Use our function to figure out the mime type
    if (fendw(".htm") || fendw(".html")) return "text/html";
    else if (fendw(".ico")) return "image/vnd.microsoft.icon";
    else if (fendw(".png")) return "image/png";
    else if (fendw(".jpg") || fendw(".jpeg")) return "image/jpeg";
    else if (fendw(".gif")) return "image/gif";
    else if (fendw(".js")) return "text/javascript";
    else if (fendw(".css")) return "text/css";
    else return "application/octet-stream";

This method wouldn’t be very interesting were it not for the Lambda Expression (remember those things I love?). The Lambda Expression takes as a string as an argument and returns a Boolean of whether the lowercase version of the provided file name ends with the specified text. In other words, it checks to see if the file has whatever extension you pass to it and ignores case. The “fendw” name, although cryptic, is short for “file ends with;” it prevented me from having to type fileName.ToLower().EndsWith(…) over and over again!

That’s it. The web server works — we just need to add a Main() method:

static void Main(string[] args)
    Console.WriteLine("On what port should the server run?:");
    int port = int.Parse(Console.ReadLine());
    Task server = new Task(() => RunServer(port)); // Start the server async
    Log("Server started on port " + port.ToString() + ". Close this window to stop it.");
    while (!server.IsCompleted) ; // Wait infinitely

The first part of this method gets the port the user wants to run the server on. I used 2013, since it’s almost New Year’s! Next, it creates an asynchronous task (using a Lambda Expression again!) to run the server on, then fires it up and blocks indefinitely. The use of the Lambda expression is unnecessary here (since the thread Main() runs on will just loop forever), but if you wanted to listen on more ports, you could create more tasks to run new instances of the server on. It makes the code more extensible, right?

It serves!

It serves!

Serving a real web page from a website I built for a charity organization last year!

Serving a real web page from a website I built for a charity organization last year!

If you desired, you could actually open a port in your router and let your friends connect to your cool new server. I should probably warn you that doing so would be a terribly risky idea, even if you only have the port open for a short while. You wouldn’t run through wolf-infested woods with a steak or swim at the Great Barrier Reef with a gaping leg wound for ten minutes, so why would you do the same on the Internet? In other words: this code is provided “AS-IS” with no express or implied warranty, especially since I do not really know how secure this server is. My guess is that it isn’t. Still, it’s hard to go wrong with a 100-line server!

Multiple File Upload Made Easy!

Uploading one file to a server is beyond easy with the help of ASP.NET, but when you add more than one file to the mix, things start getting tricky. I ran into multi-upload related issues on a recent in-company project at RMC, and rather than using a plugin, I decided to see if I could come up with something quick-and-dirty with minimal effort.

My project involved migrating a single upload to multiple uploads, so the my first step was to update my server code. Using an HttpFileCollection to retrieve files from a request will work when there is a single .NET file uploader or multiple HTML file input boxes. Here’s how you do it:

HttpFileCollection files = Request.Files;

It’s that easy. The next step is to loop through the uploaded files and save them. You can put the following in a submit button event handler:

HttpFileCollection files = Request.Files;
for(int i = 0; i < files.Count; i++)

The server-sided piece is done. Next comes the client side. For that, we will use jQuery. I realize the way I have gone about doing this isn’t the best jQuery-wise, but it is simple and it works. We will have two helper methods: one to add a file upload box and one to remove it. When we add a file upload box, we just append the relevant HTML to a parent element. When we remove a file element box, we remove the p element from its parent div. Simple enough, right?

    function AddBox(parent){
        // Add HTML for another file upload box to the DOM
        parent.append('<p>input type="file" runat="server" /><a onclick="RemoveBox(this);" href="#">Remove</a></p>');
    function RemoveBox(aContext) {
        // Get jQuery instance of "this" from the link, get its parent element
        // and remove that parent from the DOM.

Note the ‘runat=”server”‘ in AddBox. When I tested this, the files did not show up on the server without the ‘runat=”server”‘ in the input box.

All that is left now is to plug the JavaScript into your ASPX page. You’re good to go with almost no code! The only real issue with this is that if your page has to do a postback to validate, the boxes will not persist (and neither will the selected files) – this could be a big annoyance to your users. The upload will not be asynchronous, either. However, this does the trick if you need a quick, easy solution.

Here’s an example to get you started:

<div id="upload">
    <input type="button" onclick="AddBox($('upload'));" value="Add File Box" />