Ejemplo n.º 1
0
        private async Task SendSubscriptionRequest(HttpSubscriptionMetadata subscriptionMetadata, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (subscriptionMetadata == null)
            {
                throw new ArgumentNullException(nameof(subscriptionMetadata));
            }

            var endpoint = subscriptionMetadata.Endpoint;
            var topic    = subscriptionMetadata.Topic;
            var ttl      = subscriptionMetadata.TTL;

            HttpClient          httpClient          = null;
            HttpResponseMessage httpResponseMessage = null;

            try
            {
                var endpointBaseUri = endpoint.Address.WithTrailingSlash();
                httpClient = await _httpClientFactory.GetClient(endpointBaseUri, endpoint.Credentials, cancellationToken);

                var urlSafeTopicName = UrlEncoder.Encode(topic);
                var relativeUri      = $"topic/{urlSafeTopicName}/subscriber?uri={_baseUri}";
                if (ttl > TimeSpan.Zero)
                {
                    relativeUri += "&ttl=" + ttl.TotalSeconds;
                }

                var postUri = new Uri(endpointBaseUri, relativeUri);
                httpResponseMessage = await httpClient.PostAsync(relativeUri, new StringContent(""), cancellationToken);

                var status = (int?)httpResponseMessage.StatusCode;

                await _diagnosticService.EmitAsync(
                    new HttpEventBuilder(this, HttpEventType.HttpSubscriptionRequestSent)
                {
                    Uri    = postUri,
                    Topic  = subscriptionMetadata.Topic,
                    Status = status
                }.Build(), cancellationToken);

                HandleHttpErrorResponse(httpResponseMessage);
            }
            catch (TransportException)
            {
                throw;
            }
            catch (TaskCanceledException)
            {
                throw;
            }
            catch (Exception ex)
            {
                var errorMessage =
                    $"Error sending subscription request for topic {topic} of publisher {endpoint.Address}";

                TryHandleCommunicationException(ex, endpoint.Address);

                throw new TransportException(errorMessage, ex);
            }
            finally
            {
                httpResponseMessage?.Dispose();
                httpClient?.Dispose();
            }
        }
Ejemplo n.º 2
0
        /// <inheritdoc />
        public async Task Subscribe(IEndpoint endpoint, TopicName topicName, TimeSpan ttl, CancellationToken cancellationToken = default(CancellationToken))
        {
            try
            {
                var subscription = new HttpSubscriptionMetadata(endpoint, topicName, ttl);
                while (!cancellationToken.IsCancellationRequested)
                {
                    TimeSpan retryOrRenewAfter;
                    try
                    {
                        await SendSubscriptionRequest(subscription, cancellationToken);

                        if (!subscription.Expires)
                        {
                            // Subscription is not set to expire on the remote server.  Since
                            // publications are pushed to the subscribers, we don't need to keep
                            // this task running.
                            return;
                        }

                        retryOrRenewAfter = subscription.RenewalInterval;

                        _diagnosticService.Emit(
                            new HttpEventBuilder(this, DiagnosticEventType.SubscriptionRenewed)
                        {
                            Detail = "Subscription renewed.  Next renewal in " + retryOrRenewAfter,
                            Topic  = topicName,
                        }.Build());
                    }
                    catch (EndpointNotFoundException enfe)
                    {
                        // Endpoint is not defined in the supplied configuration,
                        // so we cannot determine the URI.  This is an unrecoverable
                        // error, so simply return.
                        _diagnosticService.Emit(
                            new HttpEventBuilder(this, DiagnosticEventType.EndpointNotFound)
                        {
                            Detail    = "Fatal error sending subscription request: endpoint not found",
                            Topic     = topicName,
                            Exception = enfe
                        }.Build());

                        _diagnosticService.Emit(
                            new HttpEventBuilder(this, DiagnosticEventType.SubscriptionFailed)
                        {
                            Topic = topicName
                        }.Build());

                        return;
                    }
                    catch (NameResolutionFailedException nrfe)
                    {
                        // The transport was unable to resolve the hostname in the
                        // endpoint URI.  This may or may not be a temporary error.
                        // In either case, retry after 30 seconds.
                        retryOrRenewAfter = subscription.RetryInterval;
                        _diagnosticService.Emit(
                            new HttpEventBuilder(this, HttpEventType.HttpCommunicationError)
                        {
                            Detail    = "Non-fatal error sending subscription request: unable to resolve address for hostname " + nrfe.Hostname + ".  Retry in " + retryOrRenewAfter,
                            Topic     = topicName,
                            Exception = nrfe
                        }.Build());
                    }
                    catch (ConnectionRefusedException cre)
                    {
                        // The transport was unable to resolve the hostname in the
                        // endpoint URI.  This may or may not be a temporary error.
                        // In either case, retry after 30 seconds.
                        retryOrRenewAfter = subscription.RetryInterval;
                        _diagnosticService.Emit(
                            new HttpEventBuilder(this, HttpEventType.HttpCommunicationError)
                        {
                            Detail    = "Non-fatal error sending subscription request: connection refused for " + cre.Host + ":" + cre.Port + ".  Retry in " + retryOrRenewAfter,
                            Topic     = topicName,
                            Exception = cre
                        }.Build());
                    }
                    catch (ResourceNotFoundException ire)
                    {
                        // Topic is not found.  This may be temporary.
                        retryOrRenewAfter = subscription.RetryInterval;
                        _diagnosticService.Emit(
                            new HttpEventBuilder(this, HttpEventType.HttpCommunicationError)
                        {
                            Detail    = "Non-fatal error sending subscription request: resource not found.  Topic may be misconfigured.  Retry in " + retryOrRenewAfter,
                            Topic     = topicName,
                            Exception = ire
                        }.Build());
                    }
                    catch (InvalidRequestException ire)
                    {
                        // Request is not valid.  Either the URL is malformed or the
                        // topic does not exist.  In any case, retrying would be
                        // fruitless, so just return.
                        _diagnosticService.Emit(
                            new HttpEventBuilder(this, DiagnosticEventType.EndpointNotFound)
                        {
                            Detail    = "Fatal error sending subscription request: invalid request.  Subscription abandoned.",
                            Topic     = topicName,
                            Exception = ire
                        }.Build());

                        _diagnosticService.Emit(
                            new HttpEventBuilder(this, DiagnosticEventType.SubscriptionFailed)
                        {
                            Topic = topicName
                        }.Build());

                        return;
                    }
                    catch (TransportException te)
                    {
                        // Unspecified transport error.  This may or may not be
                        // due to temporary conditions that will resolve
                        // themselves.  Retry in 30 seconds.
                        retryOrRenewAfter = subscription.RetryInterval;
                        _diagnosticService.Emit(
                            new HttpEventBuilder(this, HttpEventType.HttpCommunicationError)
                        {
                            Detail    = "Non-fatal error sending subscription request: communication error.  Retry in " + retryOrRenewAfter,
                            Topic     = topicName,
                            Exception = te
                        }.Build());
                    }
                    await Task.Delay(retryOrRenewAfter, cancellationToken);
                }
            }
            catch (OperationCanceledException)
            {
            }
        }