/// <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"); } }
/// <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 }); }
/// <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); } }
/// <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); } }
/// <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)); }
/// <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); } }
/// <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); } }