Owin Websockets - Understanding IOwinContext and WebSocketAccept

2.7k Views Asked by At

Reading through here and looking at the example here:

I am trying to understand actually what the WebSocketAccept actually does. I know that WebSocketAccept is:

using WebSocketAccept =
    Action
    <
        IDictionary<string, object>, // WebSocket Accept parameters
        Func // WebSocketFunc callback
        <
            IDictionary<string, object>, // WebSocket environment
            Task // Complete
        >
    >;

and is used in this manner:

public void Configuration(IAppBuilder app)
    {
        app.Use(UpgradeToWebSockets);
        app.UseWelcomePage();
    }

    // Run once per request
    private Task UpgradeToWebSockets(IOwinContext context, Func<Task> next)
    {
        WebSocketAccept accept = context.Get<WebSocketAccept>("websocket.Accept");
        if (accept == null)
        {
            // Not a websocket request
            return next();
        }

        accept(null, WebSocketEcho);

        return Task.FromResult<object>(null);
    }

So what is accept() actuallyt doing? Is it calling the Func<> property of the WebSocketAccept and a method WebSocketEcho is defined? WebSocketEcho is defined as:

  private async Task WebSocketEcho(IDictionary<string, object> websocketContext)

So where does the websocketContext come from? What if we wanted to pass this further down the pipeline once we have identified it is a web socket request?

1

There are 1 best solutions below

1
On BEST ANSWER

What is WebSocketAccept?

WebSocketAccept is a using alias

Example:

...
using Foo.Bar
using MyBar = Fee.Bar
...

Here we are using Bar from 2 different namespaces, but we alias the 2nd one with 'MyBar' so we can differentiate between the two.

Why use an alias as WebSocketAccept?

The alias in this case is just a convenience, so that you don't have to type the whole thing, which means that instead of writing the whole name when using it, you can use the alias instead.

Understanding WebSocketAccept

If we look closer we see that the type is:

Action<A, B>

This means that it is essentially a function that does not return and takes 2 arguments, in C# lambda:

(A, B) => { }

We see that the 1st argument (A) is: IDictionary<string, object>, which is also known as the Owin environment.

The 2nd argument is (B) is: Func<C, D> which means that it is a function that takes a C and returns a D. In C# lambda:

(C) => { return D; }

We then need to dive into the 1st argument (C) of the 2nd argument (B). And we see that it takes an Owin environment and returns a Task.

What is accept?

accept tries to extract the parameters from the IOwinContext and map them to the WebSocketAccept type.

If it can't extract them it is null and we proceed to the next middleware.

Otherwise it was a websocket request and we call the function that takes 2 parameters (WebSocketAccept), as we discussed above (Action<A, B>).

The first parameter being an ordinary dictionary, which contains the websocket accept parameters.

The second parameter being a function that takes a dictionary and returns a task.

This function is called by someone else, what the code does, is pass the callback function along to the caller.

The caller then calls the function with the correct argument. Because the caller knows the signature of the function. The function is called AFTER accepting the websocket connection request. Hence the comment callback.

What if we wanted to pass this further down the pipeline once we have identified it is a web socket request?

Well in the example, the callback function is WebSocketEcho but essentially you could pass in any function, that satisfies the function signature of:

Task MyCallbackFunction(IDictionary<string, object> context)
{
    // Do something
    return Task.FromResult(0);
}

The takeaway is that you don't call the function, the function is called for you. You specify that after negotiating the web socket request connection, you decide what happens.

The WebSocketEcho function is called once for every client, and loops until the client chooses to close the connection. Meanwhile it echo back whatever it receives.

Disclaimer: I too am just trying to wrap my head around web sockets and owin, but I wanted to share my findings for posterity, since no one had answered your question. I welcome any corrections.

EDIT
I noticed with my own experiment, that if you return from the callback function that the websocketContext connection will be Aborted. Meaning you can't send/receive messages on the connection if you pass the websocketContext around after ending the callback.

UPDATE
Last time I tried to use this on a Windows 2008 R2 IIS 7.5 server I could not get the websocket to work. Then according to this: https://stackoverflow.com/a/14130152/1640121 - The IIS 7.5 server does not support websockets.
Which means that if your application is hosted in IIS 7.5 it would not be able to have websockets.

Then I thought about a possible solution:

  1. Use a separate application, e.g. a service program (outside the IIS) that handles the websocket requests.
  2. Use a reverse proxy to map the request to the service application

This felt too cumbersome for me, which made me put aside implementing a websocket for now...