Creating Custom Server in .Net Core
Tags: dotnet
Published: 2020-11-19 22:16:16

---
title: Creating Custom Server in .Net Core
published: true
description: Creating Custom Server in .Net Core 
tags: dotnet
cover_image: https://droplet.uno/wp-content/uploads/2020/11/clientserverdiag.jpg
---


<!-- wp:paragraph -->
<p>We will be taking a look at the internal working of a Server in .Net Core by implementing our own custom server.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>To create a custom server, the custom server class must implement IServer interface and its corresponding methods. The server we will be building will bind to localhost on a port (127.0.0.1:8091), listen for HTTP request then forward the HTTP request to the rest of the request pipeline of the application. After the request is processed a corresponding response is issued.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>We will start by creating a new project, a console app project called CustomServerExample. </p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The <em>Program.cs </em>is the main entry point of the application and should look like this. </p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>  class Program
    {
        static void Main(string&#91;] args)
        {
            BuildWebHost(args).Run();
        }

        public static IWebHost BuildWebHost(string&#91;] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseCustomServer(o => o.ListeningEndpoint = @"127.0.0.1:8091")
                .UseStartup&lt;Startup>()
                .Build();
    }</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>The <em>UseCustomServer </em>method is the point of entry for our Custom Server implementation. We are also passing the endpoint at which our server will be listening for HTTP traffic.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>We will create an extension class that will define our <em>UseCustomerServer</em> Method.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>        public static class ServerExtensions
        {
            public static IWebHostBuilder UseCustomServer(this IWebHostBuilder hostBuilder, Action&lt;CustomServerOptions> options)
            {
                return hostBuilder.ConfigureServices(services =>
                {
                    services.Configure(options);
                    services.AddSingleton&lt;IServer, CustomServer>();
                });
            }
        }</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>In the extension above we registered our <em>CustomServer </em>Class for Injection into the application pool by registering a singleton of type IServer.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>With all this done we can now move into implementation of our CustomServer proper. </p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The <em>IServer </em>Interface has the following declarations: </p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>    //
    // Summary:
    //     Represents a server.
    public interface IServer : IDisposable
    {
        //
        // Summary:
        //     A collection of HTTP features of the server.
        IFeatureCollection Features { get; }

        //
        // Summary:
        //     Start the server with an application.
        //
        // Parameters:
        //   application:
        //     An instance of Microsoft.AspNetCore.Hosting.Server.IHttpApplication`1.
        //
        //   cancellationToken:
        //     Indicates if the server startup should be aborted.
        //
        // Type parameters:
        //   TContext:
        //     The context associated with the application.
        Task StartAsync&lt;TContext>(IHttpApplication&lt;TContext> application, CancellationToken cancellationToken);
        //
        // Summary:
        //     Stop processing requests and shut down the server, gracefully if possible.
        //
        // Parameters:
        //   cancellationToken:
        //     Indicates if the graceful shutdown should be aborted.
        Task StopAsync(CancellationToken cancellationToken);
    }</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>The <em>Features </em>property contains necessary HTTP features of this Server, it also contains <em>StartAsync </em>and <em>StopAsync </em>methods. The former is invoked when the server starts with the necessary application context and the latter is invoked to gracefully shut down a server.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Since our <em>CustomServer </em>class was registered as type <em>IServer </em>in the <em>ServerExtension </em>earlier declared, we will be implementing all the signatures of this interface in our class. So our CustomServer will look like below</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>    public class CustomServer : IServer
    {
        public CustomServer(IOptions&lt;CustomServerOptions> options)
        {
            Features.Set&lt;IHttpRequestFeature>(new HttpRequestFeature());
            Features.Set&lt;IHttpResponseFeature>(new HttpResponseFeature());

            var serverAddressesFeature = new ServerAddressesFeature();
            serverAddressesFeature.Addresses.Add(options.Value.ListeningEndpoint);
            Features.Set&lt;IServerAddressesFeature>(serverAddressesFeature);
        }

        public IFeatureCollection Features { get; } = new FeatureCollection();

        public void Dispose() { }

        public Task StartAsync&lt;TContext>(IHttpApplication&lt;TContext> application, CancellationToken cancellationToken)
        {
            return Task.Run(() =>
            {
                var listener = new CustomHTTPListener&lt;TContext>(application, Features);
                listener.Listen();
            });
        }
...
    }</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>In the constructor, we have the Features for both Request and Response setup also we initiate a new instance of <em>ServerAddressesFeature</em> while adding the listening endpoint from the <em>IOptions </em>injected then adding the feature to the IFeatureCollection. </p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The <em>CustomServer </em>has a server listening address now and can process HTTP request and response. Next, we move to StartAsync. </p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>When the Server starts, a new instance of <em>CustomHTTPListener</em> is created and it is responsible for listening to HTTP request. For the content in <em>CustomHTTPListener</em>;</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>    public class CustomHTTPListener&lt;TContext>
    {
        private IHttpApplication&lt;TContext> application;
        private IFeatureCollection features;
        private string uri;
        private HttpListener listener;

        public CustomHTTPListener(IHttpApplication&lt;TContext> application, IFeatureCollection features)
        {
            this.application = application;
            this.features = features;
            uri = features.Get&lt;IServerAddressesFeature>().Addresses.FirstOrDefault();
            listener = new HttpListener();
            listener.Prefixes.Add(uri);

        }

        public void Listen()
        {
            while (true)
            {
              if (listener.IsListening) return;
                ThreadPool.QueueUserWorkItem(async (_) =>
                {
                    listener.Start();
                    HttpListenerContext ctx = listener.GetContext();
                    var context = (HostingApplication.Context)(object)application.CreateContext(features);
                    context.HttpContext = new CustomHttpContext(features, ctx);
                    await application.ProcessRequestAsync((TContext)(object)context);
                    context.HttpContext.Response.OnCompleted(null, null);
                    //listener.Stop();
                });
            }
        }
    }</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>The listener constructor accepts an IHttpApplication&lt;TContext>, which is an instance of the application itself, it is the conduit to which important information is passed around the application. We also have <em>IFeatureCollection </em>as a parameter. An instance of <em>HttpListerner </em>is initialized and the uri gotten from the Application Features is added to the <em>listener</em>.<em>prefixes.add </em>method. </p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In the <em>Listen </em>method,  within the while loop,  if the Listener is listening, the iteration is exited. We add a delegate to a thread pool to help manage the listening. We then start the listener, get the listener context and pass this and the application features to <em>CustomHttpContext</em> which is intended to infuse the derivative of the <em>HttpRequest </em>and <em>HttpResponse </em>into the current application Context.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Below is what out <em>CustomHttpContext </em>looks like;</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code> public class CustomHttpContext : HttpContext
    {
        private IFeatureCollection features;
        private HttpListenerContext ctx;
        private readonly HttpRequest request;
        private readonly HttpResponse response;

        public CustomHttpContext(IFeatureCollection features, HttpListenerContext ctx)
        {
            this.Features = features;
            Request = new CustomHttpRequest(this, ctx);
            Response = new CustomHttpResponse(this, ctx);
        }

        public override IFeatureCollection Features { get; }

        public override HttpRequest Request { get;  }

        public override HttpResponse Response { get; }

.....</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p><em>CustomHttpContext</em> inherits the Abstract class <em>HttpContext</em> and we are implementing it default methods. </p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In the constructor, Request and Response property are assigned with <em>CustomHttpRequest</em> and <em>CustomHttpResponse</em> both inherit from and override the <em>HttpRequest </em>and <em>HttpResponse</em>, respectively.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Looking at <em>CustomHttpRequest</em>, we set the Method and Path properties which both needed for proper request routing within the application pipeline.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code> public class CustomHttpRequest : HttpRequest
    {
        private CustomHttpContext customHttpContext;
        public CustomHttpRequest(CustomHttpContext customHttpContext, HttpListenerContext ctx)
        {
            this.customHttpContext = customHttpContext;
            Method = ctx.Request.HttpMethod;
            Path = ctx.Request.RawUrl;
        }

        public override string Method { get; set; }
        public override string Scheme { get; set; }
        public override bool IsHttps { get; set; }
        public override HostString Host { get; set; }

....</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>In the <em>CustomHttpResponse</em> below, the OnCompleted method is triggered and the response body is read and added back to the <em>HttpListener </em>stream of response.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>...
        public override Stream Body { get; set; } = new MemoryStream();

        public override void OnCompleted(Func&lt;object, Task> callback, object state)
        {
            if (!Body.CanRead) return;
            using (var reader = new StreamReader(Body))
            {
                HttpListenerResponse response = ctx.Response;
                Body.Position = 0;
                var text = reader.ReadToEnd();
                byte&#91;] buffer = System.Text.Encoding.UTF8.GetBytes(text);
                response.ContentLength64 = Body.Length;
                response.ContentLength64 = buffer.Length;
                System.IO.Stream output = response.OutputStream;
                output.Write(buffer, 0, buffer.Length);
                output.Close();
                Body.Flush();
                Body.Dispose();
            }
        }
...</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>With all these done we are still missing the critical route handling part. So we will create <em>Startup.cs</em> and in there we add callback into the application pipeline that will help us handle the routing.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>        public void Configure(IApplicationBuilder app)
        {

            app.MapWhen(c => c.Request.Path == "/foo/bar", config =>
            {
                config.Run(async (context) =>
                {
                    context.Response.StatusCode = 200;
                    await context.Response.WriteAsync("This is Foo Bar!");
                });
            })
            .Run(async (context) =>
            {
                context.Response.StatusCode = 404;
                await context.Response.WriteAsync("Not Found");
            });
        }</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>With this, our server is done and we can give it a spin. Running our code we should see the terminal pop up with something like this.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":9,"sizeSlug":"large"} -->
<figure class="wp-block-image size-large"><img src="https://droplet.uno/wp-content/uploads/2020/11/Listener-1024x491.jpg" alt="" class="wp-image-9"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>While you necessarily wouldn't have to implement a server of your own as the default one that comes with .netcore is powerful enough and the webapi already abstract and bootstrapped things like routing and HTTP request and response handling. This post is meant to help show you what goes on under the hood. </p>
<!-- /wp:paragraph -->

The full repository of code can be found here https://github.com/omoabobade/CustomServer