Git Product home page Git Product logo

signalw's Introduction

SignalW

Even simpler and faster real-time web for ASP.NET Core.

SignalW is a simplified version of SignalR, with only WebSockets as a transport and MemoryStream as a message type.

  • WebSockets work almost everywhere to bother about long polling/SSE/any other transport.
  • Since messages could be framed, we cannot use a single buffer and need a stream to collect all chunks. We use RecyclableMemoryStream that pools internal buffers.
  • Serialization is out of scope. It is always a pain to abstract it for a general case, but in every concrete case it could be as simple as using JSON.NET (with extension methods for streams) or as flexible as a custom binary encoding.
  • Any generic WebSocket client should work. The SignalW.Client project has a WsChannel calss that wraps around the standard WebSocket class and gives methods to work with MemoryStreams instead of ArraySegments.

Instead of multiple methods inside Hubs that clients invoked by name, in SignalW we have a single method async Task OnReceiveAsync(MemoryStream payload). If one uses Angular2 with @ngrx/store then a deserialized-to-JSON message will always have a type field, and one could use a custom JsonCreationConverter<IMessage> to deserialize a message to its correct .NET type. Then one could write multiple methods with the same name that differ only by its parameter type and use dynamic keyword to dispatch a message to a correct handler.

public class DispatchHub : Hub {
    public override async Task OnReceiveAsync(MemoryStream payload) {
        // Extension method ReadJsonMessage returns IMessage object instance based on the `type` field in JSON
		object message = payload.ReadJsonMessage();
        // dispose as soon as it is no longer used becasue it uses pooled buffers inside
        payload.Dispose();

        // dynamic will dispatch to the correct method
        dynamic dynMessage = message;
        await OnReceiveAsync(dynMessage);
    }

    public async void OnReceiveAsync(MessageFoo message) {
		var stream = message.WriteJson();
        await Clients.Group("foo").InvokeAsync(stream);
    }

    public async void OnReceiveAsync(MessageBar message) {
		var stream = message.WriteJson();
        await Clients.Group("bar").InvokeAsync(stream);
    }
}

On the Angular side, one could simply use a WebSocketSubject and forward messages to @ngrx/store directly if they have a type field. No dependencies, no custom deserialization, no hassle!

Authentication with a bearer token

From a C# client

var client = new ClientWebSocket();
var header = new AuthenticationHeaderValue("Bearer", _accessToken);
client.Options.RequestHeaders.Add("Authorization", header.ToString());

From JavaScript:

It is impossible to add headers to WebSocket constructor in JavaScript, but we could use protocol parameters for this. Here we are using RxJS WebSocketSubject:

import { WebSocketSubjectConfig, WebSocketSubject } from 'rxjs/observable/dom/WebSocketSubject';
...
let wsConfig: WebSocketSubjectConfig = {
    url: 'wss://example.com/api/signalw/chat',
    protocol: [
    'access_token',
    token
    ]
};
let ws = new WebSocketSubject<any>(wsConfig);

Then in the very beginning of OWIN pipeline (before any identity middleware) use this trick to populate the correct header:

app.Use((context, next) => {
    if (!context.Request.Headers.ContainsKey("Authorization")
        && context.Request.Headers.ContainsKey("Upgrade")) {
        if (context.WebSockets.WebSocketRequestedProtocols.Count >= 2) {
            var first = context.WebSockets.WebSocketRequestedProtocols[0];
            var second = context.WebSockets.WebSocketRequestedProtocols[1];
            if (first == "access_token") {
                context.Request.Headers.Add("Authorization", "Bearer " + second);
                context.Response.Headers.Add("Sec-WebSocket-Protocol", "access_token");
            }
        }
    }
    return next();
});

To use SignalW, create a custom Hub:

[Authorize]
public class Chat : Hub {
    public override Task OnConnectedAsync() {
        if (!Context.User.Identity.IsAuthenticated) {
            Context.Connection.Channel.TryComplete();
        }
        return Task.FromResult(0);
    }

    public override Task OnDisconnectedAsync() {
        return Task.FromResult(0);
    }

    public override async Task OnReceiveAsync(MemoryStream payload) {
        await Clients.All.InvokeAsync(payload);
    }
}

Then add SignalW to the OWIN pipeline and map hubs to a path. Here we use SignalR together with MVC on the "/api" path:

public void ConfigureServices(IServiceCollection services) {
	...
	services.AddSignalW();
	...
}

public void Configure(IApplicationBuilder app, ...){
	...
	app.Map("/api/signalw", signalw => {
		signalw.UseSignalW((config) => {
			config.MapHub<Chat>("chat", Format.Text);
		});
	});
	app.Map("/api", apiApp => {
		apiApp.UseMvc();
	});
	...
}

Open several pages of https://www.websocket.org/echo.html and connect to https://[host]/api/signalw/chat?connectionId=[any value]. Each page should broadcast messages to every other page and this is a simple chat.

signalw's People

Contributors

davidfowl avatar buybackoff avatar brennanconroy avatar analogrelay avatar moozzyk avatar pranavkm avatar mikaelm12 avatar damianedwards avatar ntaylormullen avatar misterjames avatar searus avatar

Watchers

Bilgehan Zeki ÖZAYTAÇ avatar James Cloos avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.