Sanitization with Azure Service Bus Transport

Component: Azure Service Bus Transport
NuGet Package NServiceBus.Azure.Transports.WindowsAzureServiceBus (8-pre)
Target NServiceBus Version: 7.x
This page targets a pre-release version and is subject to change prior to the final release.

Prerequisites

An environment variable named AzureServiceBus.ConnectionString with the connection string for the Azure Service Bus namespace.

Azure Service Bus Transport

This sample utilizes the Azure Service Bus Transport.

Code walk-through

This sample has two endpoints

  • Publisher
  • Subscriber
public class SomeEvent :
    IEvent
{
    public Guid EventId { get; set; }
}

Publisher

Publisher publishes SomeEvent.

Subscriber

Subscriber subscribes to and handles SomeEvent. The topology used by the endpoints is ForwardingTopology, which creates a subscription named Subscriber and creates a rule for each event the endpoint subscribes to.

SomeEvent full name is Shared.Messages.In.A.Deep.Nested.Namespace.Nested.Events.SomeEvent. That is 72 characters which exceed the maximum 50 characters limit for a rule name. An attempt to use such a long rule name will result in the following exception:

Invalid Rule name 'Shared.Messages.In.A.Deep.Nested.Namespace.Nested.Events.SomeEvent' that cannot be used with Azure Service Bus. Rule name exceeds maximum allowed length or contains invalid characters. Check for invalid characters, shorten the name, or use 'Sanitization().UseStrategy()' configuration extension.`

Creating custom sanitization

Registering custom sanitization

For the purpose of this sample, custom sanitization based on a SHA1 hashing algorithm will be used:

class Sha1Sanitization :
    ISanitizationStrategy
{
    public string Sanitize(string entityPathOrName, EntityType entityType)
    {
        // remove invalid characters
        if (entityType == EntityType.Queue || entityType == EntityType.Topic)
        {
            var regexQueueAndTopicValidCharacters = new Regex(@"[^a-zA-Z0-9\-\._\/]");
            var regexLeadingAndTrailingForwardSlashes = new Regex(@"^\/|\/$");

            entityPathOrName = regexQueueAndTopicValidCharacters.Replace(entityPathOrName, string.Empty);
            entityPathOrName = regexLeadingAndTrailingForwardSlashes.Replace(entityPathOrName, string.Empty);
        }

        if (entityType == EntityType.Subscription || entityType == EntityType.Rule)
        {
            var rgx = new Regex(@"[^a-zA-Z0-9\-\._]");
            entityPathOrName = rgx.Replace(entityPathOrName, "");
        }

        var entityPathOrNameMaxLength = 0;

        switch (entityType)
        {
            case EntityType.Queue:
            case EntityType.Topic:
                entityPathOrNameMaxLength = 260;
                break;
            case EntityType.Subscription:
            case EntityType.Rule:
                entityPathOrNameMaxLength = 50;
                break;
        }

        // hash if too long
        if (entityPathOrName.Length > entityPathOrNameMaxLength)
        {
            entityPathOrName = SHA1DeterministicNameBuilder.Build(entityPathOrName);
        }

        return entityPathOrName;
    }
}

Generated hash (20 bytes) will be formatted into eight groups of 8 characters each for readability:

static class SHA1DeterministicNameBuilder
{
    public static string Build(string input)
    {
        using (var provider = new SHA1CryptoServiceProvider())
        {
            var inputBytes = Encoding.Default.GetBytes(input);
            var hashBytes = provider.ComputeHash(inputBytes);

            var hashBuilder = new StringBuilder(string.Join("", hashBytes.Select(x => x.ToString("x2"))));
            foreach (var delimeterIndex in new[]
            {
                5,
                11,
                17,
                23,
                29,
                35,
                41
            })
            {
                hashBuilder.Insert(delimeterIndex, "-");
            }
            return hashBuilder.ToString();
        }
    }
}

Custom strategy registration:

var sanitization = transport.Sanitization();
sanitization.UseStrategy<Sha1Sanitization>();

Generated rule name:

Samples

Related Articles


Last modified