예제 #1
0
        private ServiceAndMethodDefinitions Resolve(ServiceId serviceId, MethodId methodId, bool assumeExternal = false)
        {
            var result = new ServiceAndMethodDefinitions();

            if (_serviceResolver.TryResolve(serviceId, out var serviceRef))
            {
                result.Service = serviceRef.Definition;

                // NOTE: system services are not unique within a multi-service ecosystem, thus must
                // use the configuration of the calling (proxy) service without any specific method.
                // Otherwise, a continuation can be sent to a wrong instance of a system service.
                if (result.Service.Type == ServiceType.System && !string.IsNullOrEmpty(serviceId.Proxy))
                {
                    return(Resolve(new ServiceId {
                        Name = serviceId.Proxy
                    }, null, assumeExternal));
                }

                result.Method = methodId == null ? null : _methodResolver.Resolve(result.Service, methodId).Definition;
            }
            else if (assumeExternal)
            {
                var externalServiceDefinition = _externalCommunicationModel.GetOrAddService(serviceId);
                var externalMethodDefinition  = methodId == null ? null : externalServiceDefinition.GetOrAddMethod(methodId);
                result.Service = externalServiceDefinition;
                result.Method  = externalMethodDefinition;
            }
            else
            {
                throw new ServiceResolveException(serviceId);
            }

            return(result);
        }
예제 #2
0
 public static IServiceReference Resolve(this IServiceResolver resolver, ServiceId serviceId)
 {
     if (resolver.TryResolve(serviceId, out var serviceReference))
     {
         return(serviceReference);
     }
     throw new ServiceResolveException(serviceId);
 }
예제 #3
0
        public IEventPublisher GetPublisher(ServiceId serviceId, EventId eventId)
        {
            if (_eventingMethods.Count == 0)
            {
                throw new CommunicationMethodNotFoundException("There are no communication methods registered.");
            }

            IServiceDefinition serviceDefinition;
            IEventDefinition   eventDefinition;

            if (_serviceResolver.TryResolve(serviceId, out var serviceRef))
            {
                serviceDefinition = serviceRef.Definition;
                eventDefinition   = _eventResolver.Resolve(serviceDefinition, eventId).Definition;
            }
            else
            {
                throw new ServiceResolveException(serviceId);
            }

            lock (_publisherMap)
            {
                if (_publisherMap.TryGetValue(eventDefinition, out var cachedCommunicator))
                {
                    return(cachedCommunicator);
                }
            }

            var localSettings    = _communicationSettingsProvider.GetEventSettings(eventDefinition, external: false);
            var externalSettings = _communicationSettingsProvider.GetEventSettings(eventDefinition, external: true);

            var localEventingMethod    = GetEventingMethod(localSettings.CommunicationType);
            var externalEventingMethod = GetEventingMethod(externalSettings.CommunicationType);

            var localPublisher = localEventingMethod.CreateEventPublisher(GetConfiguration(eventDefinition));

            var publisher = localPublisher;

            if (externalEventingMethod.Type != localEventingMethod.Type)
            {
                var externalPublisher = externalEventingMethod.CreateEventPublisher(GetConfiguration(eventDefinition, forceExternal: true));
                publisher = new MulticastEventPublisher(localPublisher, externalPublisher);
            }

            lock (_publisherMap)
            {
                if (_publisherMap.TryGetValue(eventDefinition, out var cachedPublisher))
                {
                    (publisher as IDisposable)?.Dispose();
                    return(cachedPublisher);
                }

                _publisherMap.Add(eventDefinition, publisher);
                return(publisher);
            }
        }
예제 #4
0
 private async void InvokeInBackground(MethodInvocationData data, PublishPreferences preferences)
 {
     if (!_serviceResolver.TryResolve(data.Service, out var service) ||
         service.Definition.Type == Modeling.ServiceType.External)
     {
         var communicator = _communicatorProvider.GetCommunicator(data.Service, data.Method, assumeExternal: true);
         await communicator.InvokeAsync(data, default);
     }
     else if (!preferences.SkipLocalSubscribers)
     {
         var message = new HttpCommunicatorMessage {
             IsRetry = false
         };
         await _localMethodRunner.RunAsync(data, message);
     }
 }
 private bool IsExternal(EventDescriptor eventDesc)
 {
     return(!_serviceResolver.TryResolve(eventDesc.Service, out var serviceReference) ||
            serviceReference.Definition.Type == Modeling.ServiceType.External);
 }
예제 #6
0
        public Task <IEnumerable <IMessageListener> > StartListeningAsync(
            IConfiguration configuration,
            IServiceDefinition serviceDefinition,
            IDictionary <IEventDefinition, IConfiguration> eventConfigMap,
            CancellationToken ct)
        {
            // TODO: there are many scenarios that are not supported by the following code.
            // For example, exchange's connection is different from services' queues.

            var baseConnectionSettings = new ConnectionSettings();

            configuration.Bind(baseConnectionSettings);

            var baseConnection = _connectionManager.GetConnection(baseConnectionSettings);
            var baseChannel    = baseConnection.CreateModel();

            foreach (var eventDefinition in serviceDefinition.Events)
            {
                if (!eventConfigMap.TryGetValue(eventDefinition, out var eventConfiguration))
                {
                    eventConfiguration = configuration;
                }

                var listenerSettings = RabbitMQCommunicationMethod.CreateEventsDefaultSettings();
                eventConfiguration.Bind(listenerSettings);

                var exchangeName = listenerSettings.ExchangeName
                                   .Replace("{serviceName}", serviceDefinition.Name)
                                   .Replace("{eventName}", eventDefinition.Name);

                // TODO: declare once? what's the penalty?
                baseChannel.ExchangeDeclare(
                    exchangeName,
                    type: "fanout",
                    durable: true,
                    autoDelete: false,
                    arguments: null);

                var eventDesc = new EventDescriptor
                {
                    Service = new ServiceId {
                        Name = serviceDefinition.Name
                    },
                    Event = _eventIdProvider.GetId(eventDefinition.EventInfo)
                };

                foreach (var subscriber in _eventSubscriber.GetSubscribers(eventDesc))
                {
                    if (!_serviceResolver.TryResolve(subscriber.Service, out var subscriberServiceReference))
                    {
                        continue;
                    }

                    if (!_methodResolver.TryResolve(subscriberServiceReference.Definition, subscriber.Method, out var subscriberMethodReference))
                    {
                        continue;
                    }

                    var subscriberMethodConfiguration = _communicationModelConfiguration.GetMethodConfiguration(subscriberMethodReference.Definition, "communication");

                    var subscriberSettings = RabbitMQCommunicationMethod.CreateMethodsDefaultSettings();
                    subscriberMethodConfiguration.Bind(subscriberSettings);

                    var subscriberQueueName = subscriberSettings.QueueName
                                              .Replace("{serviceName}", subscriberServiceReference.Definition.Name)
                                              .Replace("{methodName}", subscriberMethodReference.Definition.Name);

                    // TODO: declare once? what's the penalty?
                    baseChannel.QueueBind(
                        queue: subscriberQueueName,
                        exchange: exchangeName,
                        routingKey: "",
                        arguments: null);
                }
            }

            // No direct listeners to RabbitMQ Exchanges, just configure exchange-queue bindings.

            // TODO: if events (or subscribers) use a different connection,
            // need to create a queue and a listener in that RabbitMQ instance.
            return(Task.FromResult <IEnumerable <IMessageListener> >(Array.Empty <IMessageListener>()));
        }
예제 #7
0
        public async Task StartAsync(CancellationToken ct)
        {
            foreach (var serviceDefinition in _communicationModel.Services)
            {
                if (serviceDefinition.Type == ServiceType.External)
                {
                    var serviceConfig            = _communicationModelConfiguration.GetEventsConfiguration(serviceDefinition, CommunicationSectionName);
                    var serviceCommunicationType = GetCommunicationType(serviceConfig);

                    var serviceId = new ServiceId {
                        Name = serviceDefinition.Name
                    };

                    var anySubscriberToAnyEvent = false;

                    var eventOverridesMap = new Dictionary <string, Dictionary <IEventDefinition, IConfiguration> >(StringComparer.OrdinalIgnoreCase);

                    foreach (var eventDefinition in serviceDefinition.Events)
                    {
                        var eventId   = _eventIdProvider.GetId(eventDefinition.EventInfo);
                        var eventDesc = new EventDescriptor {
                            Service = serviceId, Event = eventId
                        };

                        var subscribers = _eventSubscriber.GetSubscribers(eventDesc).ToList();

                        if (subscribers.Count == 0)
                        {
                            continue;
                        }

                        var anySubscriberToThisEvent = false;

                        foreach (var subscriber in subscribers)
                        {
                            if (_serviceResolver.TryResolve(subscriber.Service, out _))
                            {
                                anySubscriberToThisEvent = true;
                                anySubscriberToAnyEvent  = true;
                                break;
                            }
                        }

                        if (!anySubscriberToThisEvent)
                        {
                            continue;
                        }

                        if (_communicationModelConfiguration
                            .GetEventOverrideLevels(eventDefinition, CommunicationSectionName)
                            .HasFlag(ConfigOverrideLevels.Primitive))
                        {
                            var eventConfig            = _communicationModelConfiguration.GetEventConfiguration(eventDefinition);
                            var eventCommunicationType = GetCommunicationType(eventConfig);

                            if (!eventOverridesMap.TryGetValue(eventCommunicationType, out var configMap))
                            {
                                configMap = new Dictionary <IEventDefinition, IConfiguration>();
                                eventOverridesMap.Add(eventCommunicationType, configMap);
                            }

                            configMap.Add(eventDefinition, eventConfig);
                        }
                    }

                    if (anySubscriberToAnyEvent)
                    {
                        if (!eventOverridesMap.TryGetValue(serviceCommunicationType, out var configMap))
                        {
                            configMap = new Dictionary <IEventDefinition, IConfiguration>();
                        }
                        await StartListeningEventsAsync(serviceCommunicationType, serviceDefinition, serviceConfig, configMap, ct);

                        var extraCommTypes = eventOverridesMap.Keys.Where(c => !string.Equals(c, serviceCommunicationType, StringComparison.OrdinalIgnoreCase));
                        foreach (var extraCommType in extraCommTypes)
                        {
                            configMap = eventOverridesMap[extraCommType];
                            await StartListeningEventsAsync(extraCommType, serviceDefinition, serviceConfig, configMap, ct);
                        }
                    }
                }
                else
                {
                    var methodOverridesMap = new Dictionary <string, Dictionary <IMethodDefinition, IConfiguration> >(StringComparer.OrdinalIgnoreCase);
                    var eventOverridesMap  = new Dictionary <string, Dictionary <IEventDefinition, IConfiguration> >(StringComparer.OrdinalIgnoreCase);

                    foreach (var methodDefinition in serviceDefinition.Methods)
                    {
                        if (_communicationModelConfiguration
                            .GetMethodOverrideLevels(methodDefinition, CommunicationSectionName)
                            .HasFlag(ConfigOverrideLevels.Primitive))
                        {
                            var methodConfig            = _communicationModelConfiguration.GetMethodConfiguration(methodDefinition);
                            var methodCommunicationType = GetCommunicationType(methodConfig);

                            if (!methodOverridesMap.TryGetValue(methodCommunicationType, out var configMap))
                            {
                                configMap = new Dictionary <IMethodDefinition, IConfiguration>();
                                methodOverridesMap.Add(methodCommunicationType, configMap);
                            }

                            configMap.Add(methodDefinition, methodConfig);
                        }
                    }

                    foreach (var eventDefinition in serviceDefinition.Events)
                    {
                        if (_communicationModelConfiguration
                            .GetEventsOverrideLevels(serviceDefinition, CommunicationSectionName)
                            .HasFlag(ConfigOverrideLevels.Primitive))
                        {
                            var eventConfig            = _communicationModelConfiguration.GetEventConfiguration(eventDefinition);
                            var eventCommunicationType = GetCommunicationType(eventConfig);

                            if (!eventOverridesMap.TryGetValue(eventCommunicationType, out var configMap))
                            {
                                configMap = new Dictionary <IEventDefinition, IConfiguration>();
                                eventOverridesMap.Add(eventCommunicationType, configMap);
                            }

                            configMap.Add(eventDefinition, eventConfig);
                        }
                    }

                    var hasQueriesOverride = (_communicationModelConfiguration
                                              .GetQueriesOverrideLevels(serviceDefinition, CommunicationSectionName)
                                              & (ConfigOverrideLevels.BasePrimitives | ConfigOverrideLevels.ServiceTypePrimitives | ConfigOverrideLevels.ServicePrimitives)) != default;

                    var hasCommandsOverride = (_communicationModelConfiguration
                                               .GetCommandsOverrideLevels(serviceDefinition, CommunicationSectionName)
                                               & (ConfigOverrideLevels.BasePrimitives | ConfigOverrideLevels.ServiceTypePrimitives | ConfigOverrideLevels.ServicePrimitives)) != default;

                    if (hasQueriesOverride || hasCommandsOverride)
                    {
                        var allQueriesConfig            = _communicationModelConfiguration.GetQueriesConfiguration(serviceDefinition, CommunicationSectionName);
                        var allQueriesCommunicationType = GetCommunicationType(allQueriesConfig);

                        if (!methodOverridesMap.TryGetValue(allQueriesCommunicationType, out var methodConfigMap))
                        {
                            methodConfigMap = new Dictionary <IMethodDefinition, IConfiguration>();
                        }
                        await StartHadlingMethodsAsync(allQueriesCommunicationType, serviceDefinition, allQueriesConfig, methodConfigMap, ct);

                        var allCommandsConfig            = _communicationModelConfiguration.GetCommandsConfiguration(serviceDefinition, CommunicationSectionName);
                        var allCommandsCommunicationType = GetCommunicationType(allCommandsConfig);

                        if (!methodOverridesMap.TryGetValue(allCommandsCommunicationType, out methodConfigMap))
                        {
                            methodConfigMap = new Dictionary <IMethodDefinition, IConfiguration>();
                        }
                        await StartHadlingMethodsAsync(allCommandsCommunicationType, serviceDefinition, allCommandsConfig, methodConfigMap, ct);

                        var methodsExtraCommTypes = methodOverridesMap.Keys.Where(c =>
                                                                                  !string.Equals(c, allQueriesCommunicationType, StringComparison.OrdinalIgnoreCase) &&
                                                                                  !string.Equals(c, allCommandsCommunicationType, StringComparison.OrdinalIgnoreCase));
                        foreach (var methodExtraCommType in methodsExtraCommTypes)
                        {
                            methodConfigMap = methodOverridesMap[methodExtraCommType];

                            var queryConfigMap   = new Dictionary <IMethodDefinition, IConfiguration>();
                            var commandConfigMap = new Dictionary <IMethodDefinition, IConfiguration>();
                            foreach (var pair in methodConfigMap)
                            {
                                if (pair.Key.IsQuery)
                                {
                                    queryConfigMap.Add(pair.Key, pair.Value);
                                }
                                else
                                {
                                    commandConfigMap.Add(pair.Key, pair.Value);
                                }
                            }

                            if (queryConfigMap.Count > 0)
                            {
                                await StartHadlingMethodsAsync(methodExtraCommType, serviceDefinition, allQueriesConfig, queryConfigMap, ct);
                            }
                            if (commandConfigMap.Count > 0)
                            {
                                await StartHadlingMethodsAsync(methodExtraCommType, serviceDefinition, allCommandsConfig, commandConfigMap, ct);
                            }
                        }
                    }
                    else
                    {
                        var serviceConfig            = _communicationModelConfiguration.GetServiceConfiguration(serviceDefinition, CommunicationSectionName);
                        var serviceCommunicationType = GetCommunicationType(serviceConfig);

                        if (!methodOverridesMap.TryGetValue(serviceCommunicationType, out var methodConfigMap))
                        {
                            methodConfigMap = new Dictionary <IMethodDefinition, IConfiguration>();
                        }
                        await StartHadlingMethodsAsync(serviceCommunicationType, serviceDefinition, serviceConfig, methodConfigMap, ct);

                        var methodsExtraCommTypes = methodOverridesMap.Keys.Where(c => !string.Equals(c, serviceCommunicationType, StringComparison.OrdinalIgnoreCase));
                        foreach (var methodExtraCommType in methodsExtraCommTypes)
                        {
                            methodConfigMap = methodOverridesMap[methodExtraCommType];
                            await StartHadlingMethodsAsync(methodExtraCommType, serviceDefinition, serviceConfig, methodConfigMap, ct);
                        }
                    }

                    var allEventsConfig            = _communicationModelConfiguration.GetEventsConfiguration(serviceDefinition, CommunicationSectionName);
                    var allEventsCommunicationType = GetCommunicationType(allEventsConfig);

                    if (!eventOverridesMap.TryGetValue(allEventsCommunicationType, out var eventConfigMap))
                    {
                        eventConfigMap = new Dictionary <IEventDefinition, IConfiguration>();
                    }
                    await StartListeningEventsAsync(allEventsCommunicationType, serviceDefinition, allEventsConfig, eventConfigMap, ct);

                    var eventsExtraCommTypes = eventOverridesMap.Keys.Where(c => !string.Equals(c, allEventsCommunicationType, StringComparison.OrdinalIgnoreCase));
                    foreach (var eventExtraCommType in eventsExtraCommTypes)
                    {
                        eventConfigMap = eventOverridesMap[eventExtraCommType];
                        await StartListeningEventsAsync(eventExtraCommType, serviceDefinition, allEventsConfig, eventConfigMap, ct);
                    }
                }
            }
        }
예제 #8
0
        public async Task HandleAsync(PathString basePath, HttpContext context, CancellationToken ct)
        {
            var isQueryRequest   = context.Request.Method == "GET";
            var isCommandRequest = context.Request.Method == "POST";

            if (!isQueryRequest && !isCommandRequest)
            {
                await ReplyWithTextError(context.Response, 405, "Only GET and POST verbs are allowed");

                return;
            }

            var headers = context.Response.Headers;

            headers.Add(DasyncHttpHeaders.PoweredBy, "D-ASYNC");

            var basePathSegmentCount = basePath.Value.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).Length;
            var pathSegments         = context.Request.Path.Value.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).Skip(basePathSegmentCount).ToArray();

            if (pathSegments.Length == 0)
            {
                await ReplyWithTextError(context.Response, 404, "Empty request URL");

                return;
            }

            var serviceId = new ServiceId {
                Name = pathSegments[0]
            };

            if (!_serviceResolver.TryResolve(serviceId, out var serviceReference))
            {
                await ReplyWithTextError(context.Response, 404, $"Service '{serviceId.Name}' is not registered");

                return;
            }

            if (serviceReference.Definition.Type == ServiceType.External)
            {
                await ReplyWithTextError(context.Response, 404, $"Cannot invoke external service '{serviceReference.Definition.Name}' on your behalf");

                return;
            }

            if (pathSegments.Length == 1)
            {
                await ReplyWithTextError(context.Response, 404, "The request URL does not contain a service method");

                return;
            }

            var methodId = new MethodId {
                Name = pathSegments[1]
            };
            string methodIntentId = null;

            if (pathSegments.Length == 3)
            {
                methodIntentId = pathSegments[2];

                if (isQueryRequest)
                {
                    await HandleResultPoll(context, serviceReference.Id, methodId, methodIntentId);

                    return;
                }
            }

            if (pathSegments.Length > 3)
            {
                await ReplyWithTextError(context.Response, 404, "The request URL contains extra segments");

                return;
            }

            var         contentType = context.Request.GetContentType();
            ISerializer serializer;

            try
            {
                serializer = GetSerializer(contentType, isQueryRequest);
            }
            catch (ArgumentException)
            {
                await ReplyWithTextError(context.Response, 406, $"The Content-Type '{contentType.MediaType}' is not supported");

                return;
            }

            if (!_methodResolver.TryResolve(serviceReference.Definition, methodId, out var methodReference))
            {
                await ReplyWithTextError(context.Response, 404, $"The service '{serviceReference.Definition.Name}' does not have method '{methodId.Name}'");

                return;
            }

            if (isQueryRequest && !methodReference.Definition.IsQuery)
            {
                await ReplyWithTextError(context.Response, 404, $"The method '{methodId.Name}' of service '{serviceReference.Definition.Name}' is a command, but not a query, thus must be invoked with the POST verb");

                return;
            }

            MethodInvocationData   invokeData   = null;
            MethodContinuationData continueData = null;
            IValueContainer        parametersContainer;
            bool compressResponse    = false;
            bool respondWithEnvelope = false;

            if (isQueryRequest)
            {
                var inputObject = new JObject();

                foreach (var kvPair in context.Request.Query)
                {
                    if (kvPair.Value.Count == 0)
                    {
                        continue;
                    }

                    var parameterName = kvPair.Key;

                    if (kvPair.Value.Count == 1)
                    {
                        var parameterValue = kvPair.Value[0];
                        inputObject.Add(parameterName, parameterValue);
                    }
                    else
                    {
                        var values = new JArray();
                        foreach (var value in kvPair.Value)
                        {
                            values.Add(value);
                        }
                        inputObject.Add(parameterName, values);
                    }
                }

                var parametersJson = inputObject.ToString();

                parametersContainer = new SerializedValueContainer(parametersJson, _jsonSerializer);

                invokeData = new MethodInvocationData
                {
                    Service    = serviceId,
                    Method     = methodId,
                    Parameters = parametersContainer,
                    Caller     = GetCaller(context.Request.Headers)
                };
            }
            else
            {
                var payloadStream = context.Request.Body;

                var encoding = context.Request.GetContentEncoding();
                if (!string.IsNullOrEmpty(encoding))
                {
                    if ("gzip".Equals(encoding, StringComparison.OrdinalIgnoreCase))
                    {
                        compressResponse = true;
                        payloadStream    = new GZipStream(payloadStream, CompressionMode.Decompress, leaveOpen: true);
                    }
                    else if ("deflate".Equals(encoding, StringComparison.OrdinalIgnoreCase))
                    {
                        payloadStream = new DeflateStream(payloadStream, CompressionMode.Decompress, leaveOpen: true);
                    }
                    else
                    {
                        await ReplyWithTextError(context.Response, 406, $"The Content-Encoding '{encoding}' is not supported");

                        return;
                    }
                }

                var envelopeType = context.Request.Headers.GetValue(DasyncHttpHeaders.Envelope);

                if (string.IsNullOrEmpty(envelopeType))
                {
                    var payload = await payloadStream.ToBytesAsync();

                    parametersContainer = new SerializedValueContainer(payload, serializer);

                    if (string.IsNullOrEmpty(methodIntentId))
                    {
                        invokeData = new MethodInvocationData
                        {
                            Service    = serviceId,
                            Method     = methodId,
                            Parameters = parametersContainer,
                            Caller     = GetCaller(context.Request.Headers)
                        };
                    }
                    else
                    {
                        continueData = new MethodContinuationData
                        {
                            Service = serviceId,
                            Method  = methodId.CopyTo(new PersistedMethodId()),
                            Result  = parametersContainer,
                            Caller  = GetCaller(context.Request.Headers)
                        };
                        continueData.Method.IntentId = methodIntentId;
                        // TODO: get ETag from the query string
                    }
                }
                else if (envelopeType.Equals("invoke", StringComparison.OrdinalIgnoreCase))
                {
                    respondWithEnvelope = true;
                    invokeData          = await DeserializeAsync <MethodInvocationData>(serializer, payloadStream);
                }
                else if (envelopeType.Equals("continue", StringComparison.OrdinalIgnoreCase))
                {
                    respondWithEnvelope = true;
                    continueData        = await DeserializeAsync <MethodContinuationData>(serializer, payloadStream);
                }
                else
                {
                    await ReplyWithTextError(context.Response, 406, $"Unknown envelope type '{envelopeType}'");

                    return;
                }
            }

            var intentId              = context.Request.Headers.GetValue(DasyncHttpHeaders.IntentId) ?? _idGenerator.NewId();
            var externalRequestId     = context.Request.Headers.GetValue(DasyncHttpHeaders.RequestId);
            var externalCorrelationId = context.Request.Headers.GetValue(DasyncHttpHeaders.CorrelationId);
            var isRetry = context.Request.Headers.IsRetry();

            var rfc7240Preferences             = context.Request.Headers.GetRFC7240Preferences();
            var isHttpRequestBlockingExecution = !(rfc7240Preferences.RespondAsync == true);
            var waitTime = rfc7240Preferences.Wait;

            if (waitTime > MaxLongPollTime)
            {
                waitTime = MaxLongPollTime;
            }
            var waitForResult = isHttpRequestBlockingExecution || waitTime > TimeSpan.Zero;

            var communicatorMessage = new HttpCommunicatorMessage
            {
                IsRetry       = isRetry,
                RequestId     = externalRequestId,
                WaitForResult = waitForResult
            };

            if (invokeData != null)
            {
                if (invokeData.IntentId == null)
                {
                    invokeData.IntentId = intentId;
                }

                var invokeTask = _localTransitionRunner.RunAsync(invokeData, communicatorMessage);

                if (isHttpRequestBlockingExecution)
                {
                    var invocationResult = await invokeTask;

                    if (invocationResult.Outcome == InvocationOutcome.Complete)
                    {
                        await RespondWithMethodResult(context.Response, invocationResult.Result, serializer, respondWithEnvelope, compressResponse);

                        return;
                    }

                    if (waitForResult)
                    {
                        var taskResult = await TryWaitForResultAsync(
                            serviceReference.Id,
                            methodId, intentId, waitTime);

                        if (taskResult != null)
                        {
                            await RespondWithMethodResult(context.Response, taskResult, serializer, respondWithEnvelope, compressResponse);

                            return;
                        }
                    }
                }
                else
                {
                    // TODO: continue 'invokeTask' and handle exceptions in background
                }

                communicatorMessage.WaitForResult = false;

                var location = string.Concat(context.Request.Path, "/", intentId);
                context.Response.Headers.Add("Location", location);
                context.Response.Headers.Add(DasyncHttpHeaders.IntentId, intentId);
                context.Response.StatusCode = DasyncHttpCodes.Scheduled;
            }
            else if (continueData != null)
            {
                if (continueData.IntentId == null)
                {
                    continueData.IntentId = intentId;
                }

                var continueTask = _localTransitionRunner.ContinueAsync(continueData, communicatorMessage);
                // TODO: continue 'continueTask' in backgraound to handle exceptions

                context.Response.Headers.Add("Location", context.Request.Path.ToString());
                context.Response.Headers.Add(DasyncHttpHeaders.IntentId, continueData.Method.IntentId);
                context.Response.StatusCode = DasyncHttpCodes.Scheduled;
            }
        }
        private Dictionary <ConfigurationSectionKey, IConfigurationSection> ReadConfiguration(IConfigurationSection rootSection)
        {
            // [base]
            // ------------------------------------------
            // dasync

            // [base+primitives]
            // ------------------------------------------
            // dasync:commands
            // dasync:queries
            // dasync:events

            // [category]
            // ------------------------------------------
            // dasync:services:_local
            // dasync:services:_external

            // [category+primitives]
            // ------------------------------------------
            // dasync:services:_local:commands
            // dasync:services:_local:queries
            // dasync:services:_local:events
            // dasync:services:_external:commands
            // dasync:services:_external:queries
            // dasync:services:_external:events

            // [service]
            // dasync:services:{name}

            // [service+primitives]
            // dasync:services:{name}:commands:_all
            // dasync:services:{name}:queries:_all
            // dasync:services:{name}:events:_all

            // [primitives]
            // dasync:services:{name}:commands:{name}
            // dasync:services:{name}:queries:{name}
            // dasync:services:{name}:events:{name}

            var map = new Dictionary <ConfigurationSectionKey, IConfigurationSection>();

            var baseKey = new ConfigurationSectionKey();

            map[baseKey] = rootSection;

            if (rootSection == null)
            {
                return(map);
            }

            AddPrimitiveSections(rootSection, baseKey, map);

            var servicesSection = rootSection.GetSection("services");

            foreach (var serviceSection in servicesSection.GetChildren())
            {
                if (serviceSection.Key.Equals("_local", StringComparison.OrdinalIgnoreCase))
                {
                    var localServicesKey = new ConfigurationSectionKey {
                        ServiceCategory = ServiceCategory.Local
                    };
                    map[localServicesKey] = serviceSection;
                    AddPrimitiveSections(serviceSection, localServicesKey, map);
                }
                else if (serviceSection.Key.Equals("_external", StringComparison.OrdinalIgnoreCase))
                {
                    var externalServicesKey = new ConfigurationSectionKey {
                        ServiceCategory = ServiceCategory.External
                    };
                    map[externalServicesKey] = serviceSection;
                    AddPrimitiveSections(serviceSection, externalServicesKey, map);
                }
                else
                {
                    var serviceName = serviceSection.Key;
                    if (_serviceResolver.TryResolve(new ServiceId {
                        Name = serviceName
                    }, out var serviceReference))
                    {
                        serviceName = serviceReference.Definition.Name;
                    }

                    var serviceKey = new ConfigurationSectionKey {
                        ServiceName = serviceName
                    };
                    map[serviceKey] = serviceSection;

                    foreach (var subSection in serviceSection.GetChildren())
                    {
                        if (subSection.Key.Equals("commands", StringComparison.OrdinalIgnoreCase))
                        {
                            EnumerateThroughPrimitiveType(subSection, serviceKey, PrimitiveType.Command, serviceReference?.Definition, map);
                        }
                        else if (subSection.Key.Equals("queries", StringComparison.OrdinalIgnoreCase))
                        {
                            EnumerateThroughPrimitiveType(subSection, serviceKey, PrimitiveType.Query, serviceReference?.Definition, map);
                        }
                        else if (subSection.Key.Equals("events", StringComparison.OrdinalIgnoreCase))
                        {
                            EnumerateThroughPrimitiveType(subSection, serviceKey, PrimitiveType.Event, serviceReference?.Definition, map);
                        }
                    }
                }
            }

            return(map);
        }