ASP.NET Core | Open Web Interface for .NET (OWIN)

ASP.NET Core supports the Open Web Interface for .NET (OWIN). OWIN allows web apps to be decoupled from web servers. It defines a standard way for middleware to be used in a pipeline to handle requests and associated responses. ASP.NET Core applications and middleware can interoperate with OWIN-based applications, servers, and middleware.

Running OWIN middleware in the ASP.NET pipeline

ASP.NET Core’s OWIN support is deployed as part of the Microsoft.AspNetCore.Owin package. You can import OWIN support into your project by adding this package as a dependency in your project.json file:

  "dependencies": {
    "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
    "Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
    "Microsoft.AspNetCore.Owin": "1.0.0"
  },

OWIN middleware conforms to the OWIN specification, which requires a Func<IDictionary<string, object>, Task> interface, and specific keys be set (such as owin.ResponseBody). The following simple OWIN middleware displays “Hello World”:

public Task OwinHello(IDictionary<string, object> environment)
{
    string responseText = "Hello World via OWIN";
    byte[] responseBytes = Encoding.UTF8.GetBytes(responseText);

    // OWIN Environment Keys: http://owin.org/spec/spec/owin-1.0.0.html
    var responseStream = (Stream)environment["owin.ResponseBody"];
    var responseHeaders = (IDictionary<string, string[]>)environment["owin.ResponseHeaders"];

    responseHeaders["Content-Length"] = new string[] { responseBytes.Length.ToString(CultureInfo.InvariantCulture) };
    responseHeaders["Content-Type"] = new string[] { "text/plain" };

    return responseStream.WriteAsync(responseBytes, 0, responseBytes.Length);
}

The sample signature returns a Task and accepts an IDictionary<string, object> as required by OWIN.

The following code shows how to add the OwinHello middleware (shown above) to the ASP.NET pipeline with the UseOwin extension method.

public void Configure(IApplicationBuilder app)
{
    app.UseOwin(pipeline =>
    {
        pipeline(next => OwinHello);
    });
}

You can configure other actions to take place within the OWIN pipeline.

Note

Response headers should only be modified prior to the first write to the response stream.

Note

Multiple calls to UseOwin is discouraged for performance reasons. OWIN components will operate best if grouped together.

app.UseOwin(pipeline =>
{
    pipeline(next =>
    {
        // do something before
        return OwinHello;
        // do something after
    });
});

Using ASP.NET Hosting on an OWIN-based server

OWIN-based servers can host ASP.NET applications. One such server is Nowin, a .NET OWIN web server. In the sample for this article, I’ve included a project that references Nowin and uses it to create an IServer capable of self-hosting ASP.NET Core.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Owin;
using Microsoft.Extensions.Options;
using Nowin;

namespace NowinSample
{
    public class NowinServer : IServer
    {
        private INowinServer _nowinServer;
        private ServerBuilder _builder;

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

        public NowinServer(IOptions<ServerBuilder> options)
        {
            Features.Set<IServerAddressesFeature>(new ServerAddressesFeature());
            _builder = options.Value;
        }

        public void Start<TContext>(IHttpApplication<TContext> application)
        {
            // Note that this example does not take into account of Nowin's "server.OnSendingHeaders" callback.
            // Ideally we should ensure this method is fired before disposing the context. 
            Func<IDictionary<string, object>, Task> appFunc = async env =>
            {
                // The reason for 2 level of wrapping is because the OwinFeatureCollection isn't mutable
                // so features can't be added
                var features = new FeatureCollection(new OwinFeatureCollection(env));

                var context = application.CreateContext(features);
                try
                {
                    await application.ProcessRequestAsync(context);
                }
                catch (Exception ex)
                {
                    application.DisposeContext(context, ex);
                    throw;
                }

                application.DisposeContext(context, null);
            };

            // Add the web socket adapter so we can turn OWIN websockets into ASP.NET Core compatible web sockets.
            // The calling pattern is a bit different
            appFunc = OwinWebSocketAcceptAdapter.AdaptWebSockets(appFunc);

            // Get the server addresses
            var address = Features.Get<IServerAddressesFeature>().Addresses.First();

            var uri = new Uri(address);
            var port = uri.Port;
            IPAddress ip;
            if (!IPAddress.TryParse(uri.Host, out ip))
            {
                ip = IPAddress.Loopback;
            }

            _nowinServer = _builder.SetAddress(ip)
                                    .SetPort(port)
                                    .SetOwinApp(appFunc)
                                    .Build();
            _nowinServer.Start();
        }

        public void Dispose()
        {
            _nowinServer?.Dispose();
        }
    }
}

IServer is an interface that requires an Features property and a Start method.

Start is responsible for configuring and starting the server, which in this case is done through a series of fluent API calls that set addresses parsed from the IServerAddressesFeature. Note that the fluent configuration of the _builder variable specifies that requests will be handled by the appFunc defined earlier in the method. This Func is called on each request to process incoming requests.

We’ll also add an IWebHostBuilder extension to make it easy to add and configure the Nowin server.

using System;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.Extensions.DependencyInjection;
using Nowin;
using NowinSample;

namespace Microsoft.AspNetCore.Hosting
{
    public static class NowinWebHostBuilderExtensions
    {
        public static IWebHostBuilder UseNowin(this IWebHostBuilder builder)
        {
            return builder.ConfigureServices(services =>
            {
                services.AddSingleton<IServer, NowinServer>();
            });
        }

        public static IWebHostBuilder UseNowin(this IWebHostBuilder builder, Action<ServerBuilder> configure)
        {
            builder.ConfigureServices(services =>
            {
                services.Configure(configure);
            });
            return builder.UseNowin();
        }
    }
}

With this in place, all that’s required to run an ASP.NET application using this custom server to call the extension in Program.cs:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;

namespace NowinSample
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = new WebHostBuilder()
                .UseNowin()
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseIISIntegration()
                .UseStartup<Startup>()
                .Build();

            host.Run();
        }
    }
}

Learn more about ASP.NET Servers.

Run ASP.NET Core on an OWIN-based server and use its WebSockets support

Another example of how OWIN-based servers’ features can be leveraged by ASP.NET Core is access to features like WebSockets. The .NET OWIN web server used in the previous example has support for Web Sockets built in, which can be leveraged by an ASP.NET Core application. The example below shows a simple web app that supports Web Sockets and echoes back everything sent to the server through WebSockets.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
    public class Startup
    {
        public void Configure(IApplicationBuilder app)
        {
            app.Use(async (context, next) =>
            {
                if (context.WebSockets.IsWebSocketRequest)
                {
                    WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
                    await EchoWebSocket(webSocket);
                }
                else
                {
                    await next();
                }
            });

            app.Run(context =>
            {
                return context.Response.WriteAsync("Hello World");
            });
        }

        private async Task EchoWebSocket(WebSocket webSocket)
        {
            byte[] buffer = new byte[1024];
            WebSocketReceiveResult received = await webSocket.ReceiveAsync(
                new ArraySegment<byte>(buffer), CancellationToken.None);

            while (!webSocket.CloseStatus.HasValue)
            {
                // Echo anything we receive
                await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, received.Count), 
                    received.MessageType, received.EndOfMessage, CancellationToken.None);

                received = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), 
                    CancellationToken.None);
            }

            await webSocket.CloseAsync(webSocket.CloseStatus.Value, 
                webSocket.CloseStatusDescription, CancellationToken.None);
        }
    }
}

This sample is configured using the same NowinServer as the previous one - the only difference is in how the application is configured in its Configure method. A test using a simple websocket client demonstrates the application:

../_images/websocket-test.png

OWIN keys

OWIN depends on an IDictionary<string,object> object to communicate information throughout an HTTP Request/Response exchange. ASP.NET Core implements the keys listed below. See the primary specification, extensions, and OWIN Key Guidelines and Common Keys.

Request Data (OWIN v1.0.0)

KeyValue (type)Description
owin.RequestSchemeString 
owin.RequestMethodString 
owin.RequestPathBaseString 
owin.RequestPathString 
owin.RequestQueryStringString 
owin.RequestProtocolString 
owin.RequestHeadersIDictionary<string,string[]> 
owin.RequestBodyStream 

Request Data (OWIN v1.1.0)

KeyValue (type)Description
owin.RequestIdStringOptional

Response Data (OWIN v1.0.0)

KeyValue (type)Description
owin.ResponseStatusCodeintOptional
owin.ResponseReasonPhraseStringOptional
owin.ResponseHeadersIDictionary<string,string[]> 
owin.ResponseBodyStream 

Other Data (OWIN v1.0.0)

KeyValue (type)Description
owin.CallCancelledCancellationToken 
owin.VersionString 

Common Keys

KeyValue (type)Description
ssl.ClientCertificateX509Certificate 
ssl.LoadClientCertAsyncFunc<Task> 
server.RemoteIpAddressString 
server.RemotePortString 
server.LocalIpAddressString 
server.LocalPortString 
server.IsLocalbool 
server.OnSendingHeadersAction<Action<object>,object> 

SendFiles v0.3.0

KeyValue (type)Description
sendfile.SendAsyncSee delegate signaturePer Request

Opaque v0.3.0

KeyValue (type)Description
opaque.VersionString 
opaque.UpgradeOpaqueUpgradeSee delegate signature
opaque.StreamStream 
opaque.CallCancelledCancellationToken 

WebSocket v0.3.0

KeyValue (type)Description
websocket.VersionString 
websocket.AcceptWebSocketAcceptSee delegate signature.
websocket.AcceptAlt Non-spec
websocket.SubProtocolStringSee RFC6455 Section 4.2.2 Step 5.5
websocket.SendAsyncWebSocketSendAsyncSee delegate signature.
websocket.ReceiveAsyncWebSocketReceiveAsyncSee delegate signature.
websocket.CloseAsyncWebSocketCloseAsyncSee delegate signature.
websocket.CallCancelledCancellationToken 
websocket.ClientCloseStatusintOptional
websocket.ClientCloseDescriptionStringOptional

Comments

Popular posts from this blog

ASP.NET Core | Change Token

How will you improve performance of ASP.NET Core Application?