Пример #1
0
        /// <summary>
        /// Constructs a new instance of a profiler, bound to a particular thread.
        /// </summary>
        /// <param name="channel">The channel for emitter.io.</param>
        /// <param name="interval">The interval to publish measurements info.</param>
        public Instrument(string channel, TimeSpan interval) : base(interval)
        {
            this.Channel = channel;

            // Get the channel
            if (!EmitterChannel.TryParse(this.Channel, false, out Info))
            {
                throw new ArgumentException("Invalid channel");
            }
        }
Пример #2
0
        /// <summary>
        /// Attempts to generate the key and returns the result.
        /// </summary>
        /// <param name="client">The remote client.</param>
        /// <param name="channel">The full channel string.</param>
        /// <param name="message">The message to publish.</param>
        public static EmitterResponse Process(IClient client, EmitterChannel channel, KeyGenRequest request)
        {
            // Parse the channel
            EmitterChannel info;

            if (!EmitterChannel.TryParse(request.Channel, false, out info))
            {
                return(EmitterError.BadRequest);
            }

            // Should be a static (non-wildcard) channel string.
            if (info.Type != ChannelType.Static)
            {
                return(EmitterError.BadRequest);
            }

            // Attempt to parse the key, this should be a master key
            SecurityKey masterKey;

            if (!SecurityKey.TryParse(request.Key, out masterKey) || !masterKey.IsMaster || masterKey.IsExpired)
            {
                return(EmitterError.Unauthorized);
            }

            // Attempt to fetch the contract using the key. Underneath, it's cached.
            var contract = Services.Contract.GetByKey(masterKey.Contract) as EmitterContract;

            if (contract == null)
            {
                return(EmitterError.NotFound);
            }

            // Validate the contract
            if (!contract.Validate(ref masterKey))
            {
                return(EmitterError.Unauthorized);
            }

            // Generate the key
            var key = SecurityKey.Create();

            key.Master      = masterKey.Master;
            key.Contract    = contract.Oid;
            key.Signature   = contract.Signature;
            key.Permissions = request.Access;
            key.Target      = SecurityHash.GetHash(request.Channel);
            key.Expires     = request.Expires;

            return(new KeyGenResponse()
            {
                Key = key.Value,
                Channel = request.Channel
            });
        }
Пример #3
0
        /// <summary>
        /// Attempts to publish and returns the result.
        /// </summary>
        /// <param name="client">The remote client.</param>
        /// <param name="channel">The full channel string.</param>
        /// <param name="message">The message to publish.</param>
        public static EmitterEventCode Process(IClient client, string channel, ArraySegment <byte> message)
        {
            try
            {
                // Parse the channel
                EmitterChannel info;
                if (!EmitterChannel.TryParse(channel, true, out info))
                {
                    return(EmitterEventCode.BadRequest);
                }

                // Is this a special api request?
                if (HandleRequest.TryProcess(client, info, message))
                {
                    return(EmitterEventCode.Success);
                }

                // Publish should only have static channel strings
                if (info.Type != ChannelType.Static)
                {
                    return(EmitterEventCode.Forbidden);
                }

                // Attempt to parse the key
                SecurityKey key;
                if (!SecurityKey.TryParse(info.Key, out key))
                {
                    return(EmitterEventCode.BadRequest);
                }

                // Has the key expired?
                if (key.IsExpired)
                {
                    return(EmitterEventCode.Unauthorized);
                }

                // Attempt to fetch the contract using the key. Underneath, it's cached.
                var contract = Services.Contract.GetByKey(key.Contract) as EmitterContract;
                if (contract == null)
                {
                    return(EmitterEventCode.NotFound);
                }

                // Check if the payment state is valid
                if (contract.Status == EmitterContractStatus.Refused)
                {
                    return(EmitterEventCode.PaymentRequired);
                }

                // Validate the contract
                if (!contract.Validate(ref key))
                {
                    return(EmitterEventCode.Unauthorized);
                }

                // Check if the key has the permission to write here
                if (!key.HasPermission(SecurityAccess.Write))
                {
                    return(EmitterEventCode.Unauthorized);
                }

                // Check if the key has the permission for the required channel
                if (key.Target != 0 && info.Target != key.Target)
                {
                    return(EmitterEventCode.Unauthorized);
                }

                // Do we have a TTL with the message?
                int ttl;
                info.HasTimeToLive(out ttl);

                // Check if the key has a TTL and also can store (soft permission)
                if (ttl > 0 && !key.HasPermission(SecurityAccess.Store))
                {
                    ttl = 0;
                }

                // Publish within the service
                Dispatcher.Publish(contract, info.Target, info.Channel, message, ttl);

                // Successfully published
                return(EmitterEventCode.Success);
            }
            catch (NotImplementedException)
            {
                // We've got a not implemented exception
                return(EmitterEventCode.NotImplemented);
            }
            catch (Exception ex)
            {
                // We need to log it
                Service.Logger.Log(ex);

                // We've got a an internal error
                return(EmitterEventCode.ServerError);
            }
        }
Пример #4
0
        /// <summary>
        /// Occurs when the remote client attempts to subscribe to a hub.
        /// </summary>
        /// <param name="client">The remote client.</param>
        /// <param name="channel">The full channel string.</param>
        public static EmitterEventCode Process(IClient client, string channel)
        {
            try
            {
                // Parse the channel
                EmitterChannel info;
                if (!EmitterChannel.TryParse(channel, true, out info))
                {
                    return(EmitterEventCode.BadRequest);
                }

                // Simple ACK for api subscribe. We don't really need to subscribe as
                // this uses request/response topology and hence the response is sent
                // through the same TCP connection.
                if (info.Key == "emitter")
                {
                    return(EmitterEventCode.Success);
                }

                // Attempt to parse the key
                SecurityKey key;
                if (!SecurityKey.TryParse(info.Key, out key))
                {
                    return(EmitterEventCode.BadRequest);
                }

                // Has the key expired?
                if (key.IsExpired)
                {
                    return(EmitterEventCode.Unauthorized);
                }

                // Have we already subscribed?
                //if (client[channel] != null)
                //    return EmitterEventCode.Success;

                // Attempt to fetch the contract using the key. Underneath, it's cached.
                var contract = Services.Contract.GetByKey(key.Contract) as EmitterContract;
                if (contract == null)
                {
                    return(EmitterEventCode.NotFound);
                }

                // Check if the payment state is valid
                if (contract.Status == EmitterContractStatus.Refused)
                {
                    return(EmitterEventCode.PaymentRequired);
                }

                // Validate the contract
                if (!contract.Validate(ref key))
                {
                    return(EmitterEventCode.Unauthorized);
                }

                // Check if the key has the permission to read here
                if (!key.HasPermission(SecurityAccess.Read))
                {
                    return(EmitterEventCode.Unauthorized);
                }

                // Check if the key has the permission for the required channel
                if (key.Target != 0 && info.Target != key.Target)
                {
                    return(EmitterEventCode.Unauthorized);
                }

                // Subscribe to the channel
                var subs = Dispatcher.Subscribe(client, key.Contract, info.Channel, SubscriptionInterest.Messages);

                // Check if the history was also requested and we have the permission to do so
                var last = 0;
                if (!info.RequestedLast(out last) || !key.HasPermission(SecurityAccess.Load))
                {
                    return(EmitterEventCode.Success);
                }

                // Get the ssid
                var ssid = EmitterChannel.Ssid(key.Contract, info.Channel);

                // Stream the history
                Services.Storage
                .GetLastAsync(key.Contract, ssid, last)
                .ContinueWith(async(t) =>
                {
                    // Now send each message in order
                    var stream = t.Result;
                    while (stream.HasNext)
                    {
                        // Get the message asyncronously
                        var item = await stream.GetNext();
                        if (item.Count == 0)
                        {
                            continue;
                        }

                        // Increment the counter
                        Service.MessageSent?.Invoke(contract, channel, item.Count);

                        // Send the message out
                        var msg     = MqttPublishPacket.Acquire();
                        msg.Channel = info.Channel;
                        msg.Message = item;
                        client.Send(msg);
                    }
                });

                // We have successfully subscribed
                return(EmitterEventCode.Success);
            }
            catch (NotImplementedException)
            {
                // We've got a not implemented exception
                return(EmitterEventCode.NotImplemented);
            }
            catch (Exception ex)
            {
                // We need to log it
                Service.Logger.Log(ex);

                // We've got a an internal error
                return(EmitterEventCode.ServerError);
            }
        }
Пример #5
0
        /// <summary>
        /// Attempts to generate the key and returns the result.
        /// </summary>
        /// <param name="client">The remote client.</param>
        /// <param name="channel">The full channel string.</param>
        /// <param name="message">The message to publish.</param>
        public static EmitterResponse Process(IClient client, EmitterChannel channel, PresenceRequest request)
        {
            // Parse the channel
            EmitterChannel info;

            if (!EmitterChannel.TryParse(request.Channel, false, out info))
            {
                return(EmitterError.BadRequest);
            }

            // Should be a static (non-wildcard) channel string.
            if (info.Type != ChannelType.Static)
            {
                return(EmitterError.BadRequest);
            }

            // Attempt to parse the key, this should be a master key
            SecurityKey channelKey;

            if (!SecurityKey.TryParse(request.Key, out channelKey) || !channelKey.HasPermission(SecurityAccess.Presence) || channelKey.IsExpired)
            {
                return(EmitterError.Unauthorized);
            }

            // Make sure the channel name ends with
            if (!request.Channel.EndsWith("/"))
            {
                request.Channel += "/";
            }

            // Subscription
            Subscription sub;

            if (request.Changes)
            {
                // If we requested changes, register a subscription with this new interest
                Subscription.Register(client, channelKey.Contract, request.Channel, SubscriptionInterest.Presence);
            }
            else
            {
                // If the changes flag is set to false, unregister the client from presence
                Subscription.Unregister(client, channelKey.Contract, request.Channel, SubscriptionInterest.Presence, out sub);
            }

            // If we didn't request a presence, send an OK response only
            if (!request.Status)
            {
                return(new EmitterResponse(200));
            }

            // Find the subscription
            if (!Subscription.TryGet(channelKey.Contract, request.Channel, out sub))
            {
                // No subscription found, return an empty one
                return(new PresenceResponse(request.Channel, 0, null));
            }

            // Current list
            var presenceList = sub.Presence
                               .Where(p => !p.Deleted)
                               .Take(1000)
                               .Select(p => p.Value.AsInfo())
                               .ToList();

            // If we requested a presence, prepare a response
            return(new PresenceResponse(request.Channel, sub.Occupancy, presenceList));
        }
Пример #6
0
        /// <summary>
        /// Periodically publishes the usage monitors.
        /// </summary>
        private void Publish()
        {
            try
            {
                // Prepare options
                var ourContract = SecurityLicense.Current.Contract;
                var builder     = new StringBuilder();

                // Publish every monitor as a separate message
                foreach (var c in Services.Contract)
                {
                    // Get the usage from the contract
                    var contract = c as EmitterContract;
                    if (contract == null)
                    {
                        continue;
                    }

                    // If the monitor didn't change, we don't need to publish it
                    var usage = contract.Usage;
                    if (usage.IsZero)
                    {
                        continue;
                    }

                    try
                    {
                        // Reuse the builder
                        builder.Clear();

                        // Sample to the througput limiter
                        usage.MessageFrequency.Sample(usage.IncomingMessages.Value + usage.OutgoingMessages.Value);

                        // Write and reset the usage
                        usage.Write(builder, contract.Oid);
                        usage.Reset();

                        // Convert to a message, TODO: remove unnecessary allocations
                        var message = builder
                                      .ToString()
                                      .AsASCII()
                                      .AsSegment();

                        // The channel to publish into
                        var channel = "usage/" + contract.Oid + "/";

                        // Get the channel hash for query
                        EmitterChannel info;
                        if (!EmitterChannel.TryParse(channel, false, out info))
                        {
                            return;
                        }

                        // Publish the message in the cluster, don't need to store it as it might slow everything down
                        Dispatcher.Publish(ourContract, info.Target, channel, message, 60);
                    }
                    catch (Exception ex)
                    {
                        // Log the exception
                        Service.Logger.Log(ex);
                    }
                }
            }
            catch (Exception ex)
            {
                // Log the exception
                Service.Logger.Log(ex);
            }
        }
Пример #7
0
        /// <summary>
        /// Occurs when the remote client attempts to unsubscribe from a hub.
        /// </summary>
        /// <param name="client">The remote client.</param>
        /// <param name="channel">The full channel string.</param>
        public static EmitterEventCode Process(IClient client, string channel)
        {
            try
            {
                // Parse the channel
                EmitterChannel info;
                if (!EmitterChannel.TryParse(channel, true, out info))
                {
                    return(EmitterEventCode.BadRequest);
                }

                // Attempt to parse the key
                SecurityKey key;
                if (!SecurityKey.TryParse(info.Key, out key))
                {
                    return(EmitterEventCode.BadRequest);
                }

                // Has the key expired?
                if (key.IsExpired)
                {
                    return(EmitterEventCode.Unauthorized);
                }

                // Have we already unsubscribed?
                //if (client[channel] == null)
                //    return EmitterEventCode.Success;

                // Attempt to fetch the contract using the key. Underneath, it's cached.
                var contract = Services.Contract.GetByKey(key.Contract) as EmitterContract;
                if (contract == null)
                {
                    return(EmitterEventCode.NotFound);
                }

                // Check if the payment state is valid
                if (contract.Status == EmitterContractStatus.Refused)
                {
                    return(EmitterEventCode.PaymentRequired);
                }

                // Validate the contract
                if (!contract.Validate(ref key))
                {
                    return(EmitterEventCode.Unauthorized);
                }

                // Check if the key has the permission to read here
                if (!key.HasPermission(SecurityAccess.Read))
                {
                    return(EmitterEventCode.Unauthorized);
                }

                // Check if the key has the permission for the required channel
                if (info.Target != key.Target)
                {
                    return(EmitterEventCode.Unauthorized);
                }

                // Get the subscription id from the storage
                if (!Dispatcher.Unsubscribe(client, key.Contract, info.Channel, SubscriptionInterest.Messages))
                {
                    return(EmitterEventCode.Unauthorized);
                }

                // Successfully unsubscribed
                return(EmitterEventCode.Success);
            }
            catch (NotImplementedException)
            {
                // We've got a not implemented exception
                return(EmitterEventCode.NotImplemented);
            }
            catch (Exception ex)
            {
                // We need to log it
                Service.Logger.Log(ex);

                // We've got a an internal error
                return(EmitterEventCode.ServerError);
            }
        }