This recipe shows how to create a simple asynchronous HTTP server in C#.

using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;

//
// Writing an asynchronous HTTP server and client
//
// This recipe shows how to create a simple asynchronous HTTP server.
//
// How it works...
//
// Here we implement a very simple web server by using the HttpListener class.
// There is also a TcpListener class for the TCP socket I/O operations. We
// configure our listener to accept connections from any host to the local
// machine on port 1234. Then we start the listener in a separate worker thread
// so that we can control it from the main thread.
//
// The asynchronous I/O operation happens when we use the GetContextAsync
// method. Unfortunately, it does not accept CancellationToken for cancellation
// scenarios; so when we want to stop the server, we just call _listener.Abort
// method that abandons all the connection and stops the server.
//
// To perform an asynchronous request to this server, we use the HttpClient
// class located in the System.Net.Http assembly and the same namespace. We use
// the GetAsync method to issue an asynchronous HTTP GET request. There are
// methods for the other HTTP requests such as POST, DELETE, and PUT. HttpClient
// has many other options such as serializing and deserializing an object using
// different formats such as XML and JSON, specifying a proxy server address,
// credentials, and others.
//
// When you run the program, you can see that the server has been started up. In
// the server code, we use the GetContextAsync method to accept new client
// connections. This method returns when a new client connects, and we simply
// output a very basic HTML with the current date and time to the response. Then
// we request the server and print the response headers and content. You can
// also open your browser and browse to the http://localhost:1234/ URL. You will
// see the same response displayed in the browser window.
//

namespace Chapter9.Recipe2
{
    class Program
    {
        static void Main(string[] args)
        {
            var server = new AsyncHttpServer(portNumber: 1234);
            var t = Task.Run(() => server.Start());
            Console.WriteLine("Listening on port 1234. Open http://localhost:1234 in your browser.");
            Console.WriteLine("Trying to connect:");
            Console.WriteLine();

            GetResponseAsync("http://localhost:1234").GetAwaiter().GetResult();

            Console.WriteLine();
            Console.WriteLine("Press Enter to stop the server.");
            Console.ReadLine();

            server.Stop().GetAwaiter().GetResult();
        }

        static async Task GetResponseAsync(string url)
        {
            using (var client = new HttpClient())
            {
                HttpResponseMessage responseMessage = await client.GetAsync(url);
                string responseHeaders = responseMessage.Headers.ToString();
                string response = await responseMessage.Content.ReadAsStringAsync();

                Console.WriteLine("Response headers:");
                Console.WriteLine(responseHeaders);
                Console.WriteLine("Response body:");
                Console.WriteLine(response);
            }
        }

        class AsyncHttpServer
        {
            readonly HttpListener _listener;
            const string RESPONSE_TEMPLATE =
                "<html><head><title>Test</title></head><body><h2>Test page</h2><h4>Today is: {0}</h4></body></html>";

            public AsyncHttpServer(int portNumber)
            {
                _listener = new HttpListener();
                _listener.Prefixes.Add(string.Format("http://+:{0}/", portNumber));
            }

            public async Task Start()
            {
                _listener.Start();

                while (true)
                {
                    var ctx = await _listener.GetContextAsync();
                    Console.WriteLine("Client connected...");
                    var response = string.Format(RESPONSE_TEMPLATE, DateTime.Now);

                    using (var sw = new StreamWriter(ctx.Response.OutputStream))
                    {
                        await sw.WriteAsync(response);
                        await sw.FlushAsync();
                    }
                }
            }

            public async Task Stop()
            {
                _listener.Abort();
            }
        }
    }
}