Proxy generated by SignalR on the fly

Damir Dobric Posts

Next talks:

 

    

Follow me on Twitter: #ddobric



 

 

Archives

When the JavaScript renders script tag which includes/imports signal/rhubs the JavaScript proxy file will be generated. Commonly the JavaScript will include this virtual (none existing) script with following line:

<script src="/Daenet.SignalR/signalr/hubs" type="text/javascript"></script>

SignalR will receive this request and will generate the proxy classe which can be used to access hubs in the MVC manner. To make this working SignalR will generate following JavaScrip proxy on the request: http://localhost/VirtualFolder/signalr/hubs

(function ($, window) {
    /// <param name="$" type="jQuery" />
    "use strict";

    if (typeof ($.signalR) !== "function") {
        throw "SignalR: SignalR is not loaded. Please ensure jquery.signalR.js is referenced before ~/signalr/hubs.";
    }

    var hubs = {},
        signalR = $.signalR,
        callbackId = 0,
        callbacks = {};

    // Array.prototype.map
    if (!Array.prototype.hasOwnProperty("map")) {
        Array.prototype.map = function (fun, thisp) {
            var arr = this,
                i,
                length = arr.length,
                result = [];
            for (i = 0; i < length; i += 1) {
                if (arr.hasOwnProperty(i)) {
                    result = fun.call(thisp, arr, i, arr);
                }
            }
            return result;
        };
    }

    function executeCallback(hubName, fn, args, state) {
        var hub = hubs[hubName],
            hubMethod;

        if (hub) {
            signalR.hub.processState(hubName, hub.obj, state);

            if (hub[fn]) {
                hubMethod = hub.obj[fn];
                if (hubMethod) {
                    hubMethod.apply(hub.obj, args);
                }
            }
        }
    }

    function updateClientMembers(instance) {
        var newHubs = {},
            obj,
            hubName = "",
            newHub,
            memberValue,
            key,
            memberKey;

        for (key in instance) {
            if (instance.hasOwnProperty(key)) {

                obj = instance[key];

                if ($.type(obj) !== "object" ||
                        $.inArray(key, ["prototype", "constructor", "fn", "hub", "transports"]) >= 0) {
                    continue;
                }

                newHub = null;
                hubName = obj._.hubName;

                for (memberKey in obj) {
                    if (obj.hasOwnProperty(memberKey)) {
                        memberValue = obj[memberKey];

                        if (memberKey === "_" ||
                                $.type(memberValue) !== "function" ||
                                $.inArray(memberKey, obj._.ignoreMembers) >= 0) {
                            continue;
                        }

                        if (!newHub) {
                            newHub = { obj: obj };

                            newHubs[hubName] = newHub;
                        }

                        newHub[memberKey] = memberValue;
                    }
                }
            }
        }

        hubs = {};
        $.extend(hubs, newHubs);
    }

    function getArgValue(a) {
        return $.isFunction(a)
            ? null
            : ($.type(a) === "undefined"
                ? null
                : a);
    }

    function copy(obj, exclude) {
        var newObj = {};
        $.each(obj, function (key, value) {
            if ($.inArray(key, exclude) === -1) {
                // We don't use "this" because browser suck!
                newObj[key] = value;
            }
        });

        return newObj;
    }

    function serverCall(hub, methodName, args) {
        var callback = args[args.length - 1], // last argument
            methodArgs = $.type(callback) === "function"
                ? args.slice(0, -1) // all but last
                : args,
            argValues = methodArgs.map(getArgValue),
            data = { hub: hub._.hubName, action: methodName, data: argValues, state: copy(hub, ["_"]), id: callbackId },
            d = $.Deferred(),
            cb = function (result) {
                signalR.hub.processState(hub._.hubName, hub, result.State);

                if (result.Error) {
                    if (result.StackTrace) {
                        signalR.hub.log(result.Error + "\n" + result.StackTrace);
                    }
                    d.rejectWith(hub, [result.Error]);
                } else {
                    if ($.type(callback) === "function") {
                        callback.call(hub, result.Result);
                    }
                    d.resolveWith(hub, [result.Result]);
                }
            };

        callbacks[callbackId.toString()] = { scope: hub, callback: cb };
        callbackId += 1;
        hub._.connection().send(window.JSON.stringify(data));
        return d;
    }

    // Create hub signalR instance
    $.extend(signalR, {
        myHub: {
            _: {
                hubName: 'Daenet.SignalR.MyHub',
                ignoreMembers: ['doWork', 'namespace', 'ignoreMembers', 'callbacks'],
                connection: function () { return signalR.hub; }
            },

            doWork: function (message, callback) {
                return serverCall(this, "DoWork", $.makeArray(arguments));
            }
        }
    });

    signalR.hub = signalR("/Daenet.SignalR/signalr")
        .starting(function () {
            updateClientMembers(signalR);
        })
        .sending(function () {
            var localHubs = [];

            $.each(hubs, function (key) {
                var methods = [];

                $.each(this, function (key) {
                    if (key === "obj") {
                        return true;
                    }

                    methods.push(key);
                });

                localHubs.push({ name: key, methods: methods });
            });

            this.data = window.JSON.stringify(localHubs);
        })
        .received(function (result) {
            var callbackId, cb;
            if (result) {
                if (!result.Id) {
                    executeCallback(result.Hub, result.Method, result.Args, result.State);
                } else {
                    callbackId = result.Id.toString();
                    cb = callbacks[callbackId];
                    if (cb) {
                        callbacks[callbackId] = null;
                        delete callbacks[callbackId];
                        cb.callback.call(cb.scope, result);
                    }
                }
            }
        });

    signalR.hub.processState = function (hubName, left, right) {
        $.extend(left, right);
    };

} (window.jQuery, window));
0

The proxy shown above has been generated for following hub class:

public class MyHub : Hub

    {

        public void DoWork(string message)

        {

            Clients.addMessage(message);

        }      

 

    }

The JavaScript code which access the hub looks in this case like:

var myHub = $.connection.myHub;

More about hubs an GitHub: https://github.com/SignalR/SignalR/wiki/QuickStart-Hubs,
https://github.com/SignalR/SignalR/wiki/SignalR-Client-Hubs


Posted Mar 17 2012, 07:12 PM by Damir Dobric
developers.de is a .Net Community Blog powered by daenet GmbH.