WampSharp Documentation

website·Indexed 31 days ago

https://wampsharp.net/

A .NET implementation of the WAMP (Web Application Messaging Protocol) providing support for WAMPv1 and WAMPv2. It includes features for Caller, Callee, Publisher, and Subscriber roles with reflection-based, Rx-based, and raw API implementations, as well as router-side hosting via WampHost and various authentication methods including WAMP-CRA and cookie-based authentication.

Tokens
35.9K
Snippets
141
Records
173
Agent score
58%

What's inside WampSharp

  1. Access event metadata via WampEventContext

    When a method is triggered by a WAMP event, you can use WampEventContext.Current to retrieve metadata about the event, such as the PublicationId and EventDetails (which includes the publisher and the topic URI).
    public class MySubscriber
    {
        [WampTopic("com.myapp.topic1")]
        public void OnTopic1(int counter)
        {
            WampEventContext context = WampEventContext.Current;
    
            Console.WriteLine("Got event, publication ID {0}, publisher {1}: {2}",
                context.PublicationId,
                context.EventDetails.Publisher,
                counter);
        }
    }
  2. Access event metadata using WampEventContext

    In pub/sub based subscribers, WampEventContext.Current provides access to details about the currently received event, such as the PublicationId and EventDetails.Publisher.
    public class MySubscriber
    {
        [WampTopic("com.myapp.topic1")]
        public void OnTopic1(int counter)
        {
            WampEventContext context = WampEventContext.Current;
            Console.WriteLine($"Got event, publication ID {context.PublicationId}, publisher {context.EventDetails.Publisher}: {counter}");
        }
    }
  3. Understand WAMPv2 Realms

    A WAMP realm acts as a domain where URIs are mapped to specific procedures and topics. In WampSharp, the API is structured around this realm concept to manage how routers and clients access application components.
  4. Access invocation details via WampInvocationContext

    The WampInvocationContext.Current.InvocationDetails object provides metadata about the current call, such as the caller's identification (if disclosed) and whether the caller requested progressive call results (ReceiveProgress).
    public async Task<int> LongOp(int n, IProgress<int> progress)
    {
        InvocationDetails details = WampInvocationContext.Current.InvocationDetails;
    
        for (int i = 0; i < n; i++)
        {
            if (details.ReceiveProgress == true)
            {
                progress.Report(i);
            }
            await Task.Delay(100).ConfigureAwait(false);
        }
        return n;
    }
  5. Use WampSharp with .NET Standard 1.3

    WampSharp supports .NET Standard 1.3, making it compatible with .NET Framework 4.6, .NET Core, Universal Windows Platform (UWP) 10, and Mono/Xamarin platforms. Currently, only WAMPv2 is supported in this version. Both JSON and MsgPack bindings, as well as RawSocket support, are available.
  6. Subscribe to a WAMP topic using Rx-based Subscriber

    The Rx-based Subscriber allows you to subscribe to events of a WAMP router realm topic using the Reactive Extensions (Rx) Observable API. By using GetSubject<T>, you can receive events of a specific type. Note that if a generic type is specified, only the first argument in the Arguments parameter of the WAMP EVENT message is deserialized.
    DefaultWampChannelFactory factory = new DefaultWampChannelFactory();
    const string serverAddress = "ws://127.0.0.1:8080/ws";
    IWampChannel channel = factory.CreateJsonChannel(serverAddress, "realm1");
    await channel.Open().ConfigureAwait(false);
    
    IWampRealmProxy realmProxy = channel.RealmProxy;
    
    // Subscribe to a topic expecting int events
    IDisposable subscription = realmProxy.Services.GetSubject<int>("com.myapp.topic1")
        .Subscribe(x =>
        {
            Console.WriteLine($"Got Event: {x}");
        });
    
    // Release the WebSocket thread to prevent blocking
    await Task.Yield();
    
    // To unsubscribe
    subscription.Dispose();
  7. Implement a low-level RPC callee using IWampRpcOperation

    WampSharp provides a 'Raw Callee' API for developers who need to handle RPC operations exactly as they are sent and received, bypassing the reflection-based API. To use this, implement the IWampRpcOperation interface. You will receive an IWampFormatter to manually deserialize method parameters and must use the IWampRawRpcOperationRouterCallback (the caller object) to send back a result or an error.
    public class Add2Operation : IWampRpcOperation
    {
        public string Procedure => "com.arguments.add2";
    
        public IWampCancellableInvocation Invoke<TMessage>(IWampRawRpcOperationRouterCallback caller, IWampFormatter<TMessage> formatter, TMessage[] arguments)
        {
            int x = formatter.Deserialize<int>(arguments[0]);
            int y = formatter.Deserialize<int>(arguments[1]);
            int result = x + y;
    
            YieldOptions dummyDetails = new YieldOptions();
            caller.Result(WampObjectFormatter.Value, dummyDetails, new object[] { result });
            return null;
        }
    
        // Note: You must also implement other Invoke overloads for keyword arguments and basic signatures
        public IWampCancellableInvocation Invoke<TMessage>(IWampRawRpcOperationRouterCallback caller, IWampFormatter<TMessage> formatter) { /* handle error */ return null; }
        public IWampCancellableInvocation Invoke<TMessage>(IWampRawRpcOperationRouterCallback caller, IWampFormatter<TMessage> formatter, TMessage[] arguments, IDictionary<string, TMessage> argumentsKeywords) { /* handle */ return null; }
    }
  8. Publish single-value events using Rx-based Publisher

    The Rx-based Publisher allows you to publish events to a WAMP router realm topic using the Reactive Extensions Observer API. By calling GetSubject<T> on the realm services, you obtain an ISubject<T> where calling OnNext(value) publishes that value as the single argument of a WAMP EVENT message.
    DefaultWampChannelFactory channelFactory = new DefaultWampChannelFactory();
    IWampChannel channel = channelFactory.CreateJsonChannel("ws://127.0.0.1:8080/ws", "realm1");
    await channel.Open().ConfigureAwait(false);
    
    IWampRealmProxy realm = channel.RealmProxy;
    ISubject<int> subject = realm.Services.GetSubject<int>("com.myapp.topic1");
    
    // Publishes 5 to com.myapp.topic1
    subject.OnNext(5);
  9. Implement cookie-based router-side authentication in WampSharp

    To implement cookie-based authentication in a WAMP router, you must provide an implementation of the ICookieAuthenticatorFactory interface to the DefaultWampAuthenticationHost (or Fleck) constructor. The router uses this factory to create a session authenticator based on the cookies provided by the transport layer.
    // 1. Implement the Factory
    internal class MyCookieAuthenticatorFactory : ICookieAuthenticatorFactory
    {
        public IWampSessionAuthenticator CreateAuthenticator(ICookieProvider cookieProvider)
        {
            return new MyCookieAuthenticator(cookieProvider);
        }
    }
    
    // 2. Implement the Authenticator
    internal class MyCookieAuthenticator : WampSessionAuthenticator
    {
        public MyCookieAuthenticator(ICookieProvider cookieProvider)
        {
            Cookie cookie = cookieProvider.GetCookieByName("beetle");
            if (cookie != null)
            {
                this.Beetle = cookie.Value;
                // Assign an authorizer based on the cookie value
                this.Authorizer = mBeetleToAuthorizers[Beetle];
                this.IsAuthenticated = true;
            }
        }
    
        public override string AuthenticationId => Beetle;
        public override string AuthenticationMethod => "cookie";
        public string Beetle { get; set; }
    
        public override void Authenticate(string signature, AuthenticateExtraData extra)
        {
            throw new WampAuthenticationException("Cookie wasn't present");
        }
    }
    
    // 3. Pass the factory to the Host
    DefaultWampAuthenticationHost host = new DefaultWampAuthenticationHost(
        "ws://0.0.0.0:8080/ws",
        new MyAuthenticationFactory(),
        new MyCookieAuthenticatorFactory()
    );
    
  10. Register a Reflection-based Callee service

    Use the [WampProcedure] attribute to mark methods in a class or interface as WAMP remote procedures. Register the class instance using realm.Services.RegisterCallee(instance). The registration returns an IAsyncDisposable which must be disposed to unregister the service.
    public interface IArgumentsService
    {
        [WampProcedure("com.arguments.ping")]
        void Ping();
    
        [WampProcedure("com.arguments.add2")]
        int Add2(int a, int b);
    }
    
    public class ArgumentsService : IArgumentsService
    {
        public void Ping() { }
        public int Add2(int a, int b) => a + b;
    }
    
    // Registration
    IArgumentsService instance = new ArgumentsService();
    IWampRealmProxy realm = channel.RealmProxy;
    IAsyncDisposable disposable = await realm.Services.RegisterCallee(instance).ConfigureAwait(false);
    
    // To unregister
    await disposable.DisposeAsync().ConfigureAwait(false);
  11. Configure vtortola.WebSocketListener transport

    To use the vtortola.WebSocketListener transport, install the WampSharp.Vtortola package. This transport supports per-message deflate compression, which can be enabled via the perMessageDeflate constructor parameter.
    WampHost host = new WampHost();
    
    IWampTransport transport = 
        new VtortolaWebSocketTransport
            (endpoint: new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080),
                perMessageDeflate: true);
    
    host.RegisterTransport(transport,
        new JTokenJsonBinding(),
        new JTokenMsgpackBinding());
    
    host.Open();
  12. Handle complex WAMP events using Tuples and WampEventValueTupleConverter

    To handle multiple parameters (positional or keyword) in a WAMP EVENT message, use a custom converter deriving from WampEventValueTupleConverter<T>, where T is the desired tuple type. This converter is passed to the GetSubject method of IWampRealmServiceProvider to return an ISubject<T> of the specified tuple type.
    // Define converters for specific tuple shapes
    public class MyPositionalTupleEventConverter : WampEventValueTupleConverter<(int, int)> { }
    public class MyKeywordTupleEventConverter : WampEventValueTupleConverter<(int number1, int number2, string c, ComplexContract d)> { }
    
    // Usage in Main
    ISubject<(int, int)> topic1Subject = channel.RealmProxy.Services.GetSubject
        ("com.myapp.topic1", new MyPositionalTupleEventConverter());
    
    topic1Subject.Subscribe(value =>
    {
        (int number1, int number2) = value;
        Console.WriteLine($">com.myapp.topic1: Got event: number1:{number1}, number2:{number2}");
    });
    
    ISubject<(int number1, int number2, string c, ComplexContract d)> topic2Subject = 
        channel.RealmProxy.Services.GetSubject("com.myapp.topic2", new MyKeywordTupleEventConverter());
    
    topic2Subject.Subscribe(value =>
    {
        (int number1, int number2, string c, ComplexContract d) = value;
        Console.WriteLine($">com.myapp.topic2: Got event: number1:{number1}, number2:{number2}, c:{c}, d:{d}");
    });