Skip to main content

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;

namespace AsyncHttpServerAndClient
{
    internal static class Program
    {
        internal static void Main(string[] args)
        {
            MainAsync(args).GetAwaiter().GetResult();
        }

        private static async Task MainAsync(string[] args)
        {
            var tcs = new TaskCompletionSource<object>();

            Console.CancelKeyPress += (sender, e) => { tcs.SetResult(null); };

            var server = new AsyncHttpServer(portNumber: 1234);
            var task = Task.Run(() => server.Start());

            await Console.Out.WriteLineAsync("Listening on port 1234. Open http://localhost:1234 in your browser.");
            await Console.Out.WriteLineAsync("Trying to connect:");
            await Console.Out.WriteLineAsync();

            await GetResponseAsync("http://localhost:1234");
            await Console.Out.WriteLineAsync("Press Ctrl+C to stop the server...");

            await tcs.Task;
            await server.Stop();
        }

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

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

        private class AsyncHttpServer
        {
            private const string ResponseTemplate =
                "<html>" +
                    "<head>" +
                        "<title>Test</title>" +
                    "</head>" +
                    "<body>" +
                        "<h2>Test page</h2>" +
                        "<h4>Today is: {0}</h4>" +
                    "</body>" +
                "</html>";

            private readonly HttpListener _listener;

            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.Out.WriteLine("Client connected...");
                    Console.Out.WriteLine("Serving file: '{0}'", ctx.Request.Url);

                    ctx.Response.Headers.Add("content-type: text/html; charset=UTF-8");
                    ctx.Response.Headers.Add("x-content-type-options: nosniff");
                    ctx.Response.Headers.Add("x-xss-protection:1; mode=block");
                    ctx.Response.Headers.Add("x-frame-options:DENY");
                    ctx.Response.Headers.Add("cache-control:no-store, no-cache, must-revalidate");
                    ctx.Response.Headers.Add("pragma:no-cache");
                    ctx.Response.Headers.Add("Server", "jl");

                    var response = string.Format(ResponseTemplate, DateTime.Now);
                    using (var sw = new StreamWriter(ctx.Response.OutputStream))
                    {
                        await sw.WriteAsync(response);
                        await sw.FlushAsync();
                    }
                }
            }

            public async Task Stop()
            {
                await Console.Out.WriteLineAsync(
                    "Stopping server...");

                if (_listener.IsListening)
                {
                    _listener.Stop();
                    _listener.Close();
                }
            }
        }
    }
}