Exemple #1
0
    public void FabricServiceEndpointSelector_SelectsEmptyListenerEndpoint_EmptyListenerName()
    {
        var serviceName = new Uri("fabric:/Application/Service");
        var emptyStringMatchesAnyListener = false;
        var listenerName    = string.Empty;
        var allowedScheme   = "https";
        var endpointAddress = "https://*****:*****@"{{
            'Endpoints': {{
                'DifferentServiceEndpoint1': 'https://localhost:123/query',
                'DifferentServiceEndpoint2': 'https://loopback:123/query',
                '{listenerName}': '{endpointAddress}',
                'DifferentServiceEndpoint3': 'https://localhost:456/query',
                'DifferentServiceEndpoint4': 'https://loopback:456/query'
            }}
        }}".Replace("'", "\"");

        var fabricServiceEndpoint = new FabricServiceEndpoint(
            listenerNames: new[] { listenerName },
            allowedSchemePredicate: (scheme) => scheme == allowedScheme,
            emptyStringMatchesAnyListener: emptyStringMatchesAnyListener);

        ServiceEndpointCollection.TryParseEndpointsString(endpoints, out var serviceEndpointCollection);

        FabricServiceEndpointSelector.TryGetEndpoint(fabricServiceEndpoint, serviceEndpointCollection, out var endpointUri)
        .Should().BeTrue("There should be a matching endpoint");
        endpointUri.ToString().Should().BeEquivalentTo(endpointAddress);
    }
Exemple #2
0
    public void FabricServiceEndpointSelector_ReturnsFalseOnMalformedUri_EmptyListenerName()
    {
        var serviceName = new Uri("fabric:/Application/Service");
        var emptyStringMatchesAnyListener = true;
        var listenerName    = string.Empty;
        var allowedScheme   = "https";
        var endpointAddress = "/alsoMalformed";

        var endpoints = $@"{{
            'Endpoints': {{
                'DifferentServiceEndpoint1': '/malformed',
                'DifferentServiceEndpoint2': '/malformed',
                '{listenerName}': '{endpointAddress}',
                'DifferentServiceEndpoint3': '/malformed',
                'DifferentServiceEndpoint4': '/malformed'
            }}
        }}".Replace("'", "\"");

        var fabricServiceEndpoint = new FabricServiceEndpoint(
            listenerNames: new[] { listenerName },
            allowedSchemePredicate: (scheme) => scheme == allowedScheme,
            emptyStringMatchesAnyListener: emptyStringMatchesAnyListener);

        ServiceEndpointCollection.TryParseEndpointsString(endpoints, out var serviceEndpointCollection);

        FabricServiceEndpointSelector.TryGetEndpoint(fabricServiceEndpoint, serviceEndpointCollection, out var endpointUri)
        .Should().BeFalse("There should be no matching endpoint");
        endpointUri.Should().BeNull();
    }
Exemple #3
0
    public void FabricServiceEndpointSelector_SelectsEndpointInOrdinalStringOrder_EmptyListenerName()
    {
        var serviceName = new Uri("fabric:/Application/Service");
        var emptyStringMatchesAnyListener = true;
        var allowedScheme = "https";

        var endpoints             = $@"{{
            'Endpoints': {{
                'SelectedServiceEndpoint': 'https://localhost:123/selected',
                'notSelected1': 'https://loopback:123/query',
                'notSelected2': 'https://localhost:456/query',
                'notSelected3': 'https://loopback:456/query'
            }}
        }}".Replace("'", "\"");
        var fabricServiceEndpoint = new FabricServiceEndpoint(
            listenerNames: new[] { string.Empty },
            allowedSchemePredicate: (scheme) => scheme == allowedScheme,
            emptyStringMatchesAnyListener: emptyStringMatchesAnyListener);

        ServiceEndpointCollection.TryParseEndpointsString(endpoints, out var serviceEndpointCollection);

        FabricServiceEndpointSelector.TryGetEndpoint(fabricServiceEndpoint, serviceEndpointCollection, out var endpointUri)
        .Should().BeTrue("There should be a matching endpoint");
        endpointUri.ToString().Should().BeEquivalentTo("https://localhost:123/selected");
    }
Exemple #4
0
    public void FabricServiceEndpointSelector_NoValidEndpointBasedOnScheme_NamedListener()
    {
        var serviceName = new Uri("fabric:/Application/Service");
        var emptyStringMatchesAnyListener = true;
        var listenerName  = "ServiceEndpointSecure";
        var allowedScheme = "https";

        var endpoints = $@"{{
            'Endpoints': {{
                'DifferentServiceEndpoint1': '/malformed',
                '{listenerName}': 'http://localhost/invalidScheme',
            }}
        }}".Replace("'", "\"");

        var fabricServiceEndpoint = new FabricServiceEndpoint(
            listenerNames: new[] { listenerName },
            allowedSchemePredicate: (scheme) => scheme == allowedScheme,
            emptyStringMatchesAnyListener: emptyStringMatchesAnyListener);

        ServiceEndpointCollection.TryParseEndpointsString(endpoints, out var serviceEndpointCollection);

        FabricServiceEndpointSelector.TryGetEndpoint(fabricServiceEndpoint, serviceEndpointCollection, out var endpointUri)
        .Should().BeFalse("There should be no matching endpoint because of scheme mismatch.");
        endpointUri.Should().BeNull();
    }
Exemple #5
0
    public void FabricServiceEndpointSelector_SelectsEndpointBasedOnScheme_MultipleRequestedListeners()
    {
        var serviceName = new Uri("fabric:/Application/Service");
        var emptyStringMatchesAnyListener = true;
        var listenerNames   = new[] { "ServiceEndpointSecure", string.Empty };
        var allowedScheme   = "https";
        var endpointAddress = "https://*****:*****@"{{
            'Endpoints': {{
                'DifferentServiceEndpoint1': '/malformed',
                'ServiceEndpointSecure': 'http://localhost/invalidScheme',
                'ValidServiceEndpoint': '{endpointAddress}'
            }}
        }}".Replace("'", "\"");

        var fabricServiceEndpoint = new FabricServiceEndpoint(
            listenerNames: listenerNames,
            allowedSchemePredicate: (scheme) => scheme == allowedScheme,
            emptyStringMatchesAnyListener: emptyStringMatchesAnyListener);

        ServiceEndpointCollection.TryParseEndpointsString(endpoints, out var serviceEndpointCollection);

        FabricServiceEndpointSelector.TryGetEndpoint(fabricServiceEndpoint, serviceEndpointCollection, out var endpointUri)
        .Should().BeTrue("There should be a matching endpoint");
        endpointUri.ToString().Should().BeEquivalentTo(endpointAddress);
    }
Exemple #6
0
    public void FabricServiceEndpointSelector_NoMatchingEndpointScheme()
    {
        var serviceName = new Uri("fabric:/Application/Service");
        var emptyStringMatchesAnyListener = true;
        var listenerName    = "ServiceEndpoint";
        var allowedScheme   = "http";
        var endpointAddress = "https://*****:*****@"{{
            'Endpoints': {{
                'DifferentServiceEndpoint1': 'https://localhost:123/query',
                'DifferentServiceEndpoint2': 'https://loopback:123/query',
                '{listenerName}': '{endpointAddress}',
                'DifferentServiceEndpoint3': 'https://localhost:456/query',
                'DifferentServiceEndpoint4': 'https://loopback:456/query'
            }}
        }}".Replace("'", "\"");

        var fabricServiceEndpoint = new FabricServiceEndpoint(
            listenerNames: new[] { listenerName },
            allowedSchemePredicate: (scheme) => scheme == allowedScheme,
            emptyStringMatchesAnyListener: emptyStringMatchesAnyListener);

        ServiceEndpointCollection.TryParseEndpointsString(endpoints, out var serviceEndpointCollection);

        FabricServiceEndpointSelector.TryGetEndpoint(fabricServiceEndpoint, serviceEndpointCollection, out var endpointUri)
        .Should().BeFalse("No matching endpoint with specified scheme.");
        endpointUri.Should().BeNull();
    }
Exemple #7
0
        public void FabricServiceEndpointSelector_NoExceptionOnMalformedUri_EmptyListener()
        {
            // Arrange
            var serviceName = new Uri("fabric:/Application/Service");
            var emptyStringMatchesAnyListener = true;
            var listenerName    = string.Empty;
            var allowedScheme   = "https";
            var endpointAddress = "https://*****:*****@"{{
                'Endpoints': {{
                    'DifferentServiceEndpoint1': '/malformed',
                    'DifferentServiceEndpoint2': '/malformed',
                    'ValidServiceEndpoint': '{endpointAddress}',
                    'DifferentServiceEndpoint3': '/malformed',
                    'DifferentServiceEndpoint4': '/malformed'
                }}
            }}".Replace("'", "\"");

            var fabricServiceEndpoint = new FabricServiceEndpoint(
                listenerNames: new[] { listenerName },
                allowedSchemePredicate: (scheme) => scheme == allowedScheme,
                emptyStringMatchesAnyListener: emptyStringMatchesAnyListener);

            ServiceEndpointCollection.TryParseEndpointsString(endpoints, out var serviceEndpointCollection);

            // Act + Assert
            FabricServiceEndpointSelector.TryGetEndpoint(fabricServiceEndpoint, serviceEndpointCollection, out var endpointUri)
            .Should().BeTrue("There should be a matching endpoint");
            endpointUri.ToString().Should().BeEquivalentTo(endpointAddress);
        }
Exemple #8
0
    private DestinationConfig BuildDestination(ReplicaWrapper replica, string listenerName, string healthListenerName, PartitionWrapper partition)
    {
        if (!ServiceEndpointCollection.TryParseEndpointsString(replica.ReplicaAddress, out var serviceEndpointCollection))
        {
            throw new ConfigException($"Could not parse endpoints for replica {replica.Id}.");
        }

        // TODO: FabricServiceEndpoint has some other fields we are ignoring here. Decide which ones are relevant and fix this call.
        var serviceEndpoint = new FabricServiceEndpoint(
            listenerNames: new[] { listenerName },
            allowedSchemePredicate: HttpsSchemeSelector,
            emptyStringMatchesAnyListener: true);
        if (!FabricServiceEndpointSelector.TryGetEndpoint(serviceEndpoint, serviceEndpointCollection, out var endpointUri))
        {
            throw new ConfigException($"No acceptable endpoints found for replica '{replica.Id}'. Search criteria: listenerName='{listenerName}', emptyStringMatchesAnyListener=true.");
        }

        // Get service endpoint from the health listener, health listener is optional.
        Uri healthEndpointUri = null;
        if (!string.IsNullOrEmpty(healthListenerName))
        {
            var healthEndpoint = new FabricServiceEndpoint(
                listenerNames: new[] { healthListenerName },
                allowedSchemePredicate: HttpsSchemeSelector,
                emptyStringMatchesAnyListener: true);
            if (!FabricServiceEndpointSelector.TryGetEndpoint(healthEndpoint, serviceEndpointCollection, out healthEndpointUri))
            {
                throw new ConfigException($"No acceptable health endpoints found for replica '{replica.Id}'. Search criteria: listenerName='{healthListenerName}', emptyStringMatchesAnyListener=true.");
            }
        }

        return new DestinationConfig
        {
            Address = endpointUri.AbsoluteUri,
            Health = healthEndpointUri?.AbsoluteUri,
            Metadata = new Dictionary<string, string>
            {
                { "PartitionId", partition.Id.ToString() ?? string.Empty },
                { "NamedPartitionName", partition.Name ?? string.Empty },
                { "ReplicaId", replica.Id.ToString() ?? string.Empty }
            }
        };
    }
Exemple #9
0
    /// <summary>
    /// Selects and endpoint (aka "listener") from a presented <paramref name="endpoints"/> collection
    /// that satisfies all constraints of <paramref name="fabricServiceEndpoint"/>.
    /// </summary>
    /// <param name="fabricServiceEndpoint">User-defined info and constraints for the selecting an endpoint from <paramref name="fabricServiceEndpoint"/>.</param>
    /// <param name="endpoints">Collection of endpoints to choose from.</param>
    /// <param name="endpointUri">The endpoint URI to extract.</param>
    /// <returns>Boolean indicating whether an endpoint URI was successfully retrieved.</returns>
    public static bool TryGetEndpoint(
        FabricServiceEndpoint fabricServiceEndpoint,
        ServiceEndpointCollection endpoints,
        out Uri endpointUri)
    {
        _ = fabricServiceEndpoint ?? throw new ArgumentNullException(nameof(fabricServiceEndpoint));
        _ = endpoints ?? throw new ArgumentNullException(nameof(endpoints));

        endpointUri = null;
        string endpointAddress = null;

        foreach (var listenerName in fabricServiceEndpoint.ListenerNames)
        {
            // SF Reverse Proxy endpoint selection logic: https://github.com/microsoft/service-fabric/blob/1e118f02294c99b61e676c07ac97283ee12197d4/src/prod/src/Management/ApplicationGateway/Http/ServiceEndpointsList.cpp#L52
            if (listenerName == string.Empty && fabricServiceEndpoint.EmptyStringMatchesAnyListener)
            {
                endpointUri = endpoints.ToReadOnlyDictionary()

                              // NOTE: Ordinal comparison used to match sort order of Service Fabric Reverse Proxy
                              .OrderBy(listenerAddressPair => listenerAddressPair.Key, StringComparer.Ordinal)

                              // From the endpoints above, select endpoints with valid URIs
                              .Select(listenerAddressPair =>
                {
                    if (Uri.TryCreate(listenerAddressPair.Value, UriKind.Absolute, out var uri))
                    {
                        return(uri);
                    }

                    return(null);
                })
                              .Where(replicaAddress => replicaAddress != null)

                              // Pick first endpoint that matches scheme predicate.
                              .FirstOrDefault(replicaAddress =>
                {
                    if (fabricServiceEndpoint.AllowedSchemePredicate(replicaAddress.Scheme))
                    {
                        return(true);
                    }

                    return(false);
                });

                // Bail as soon as first valid endpoint is found.
                if (endpointUri != null)
                {
                    // CoreFrameworkFabricTrace.Instance.TraceVerbose("Located endpoint URI is '{0}'", endpointUri);
                    return(true);
                }
            }
            else
            {
                // Pick named listener endpoint
                if (endpoints.TryGetEndpointAddress(listenerName: listenerName, endpointAddress: out endpointAddress))
                {
                    if (!Uri.TryCreate(endpointAddress, UriKind.Absolute, out var endpointUri_))
                    {
                        continue;
                    }

                    // Match the Uri against allowed scheme predicate.
                    if (!fabricServiceEndpoint.AllowedSchemePredicate(endpointUri_.Scheme))
                    {
                        continue;
                    }

                    endpointUri = endpointUri_;

                    // CoreFrameworkFabricTrace.Instance.TraceVerbose("Located endpoint URI is '{0}'", endpointUri);
                    return(true);
                }
            }
        }
        return(false);
    }