Exemple #1
0
        /// <summary>
        /// Creates a <see cref="JObject"/> used as the <see cref="HttpRequestMessage"/> entity body for a <see cref="WebHook"/>.
        /// </summary>
        /// <param name="workItem">The <see cref="WebHookWorkItem"/> representing the data to be sent.</param>
        /// <returns>An initialized <see cref="JObject"/>.</returns>
        protected virtual JObject CreateWebHookRequestBody(WebHookWorkItem workItem)
        {
            if (workItem == null)
            {
                throw new ArgumentNullException(nameof(workItem));
            }

            // Set notifications
            var webhookBody = new WebhookBody
            {
                Id      = workItem.Id,
                Attempt = workItem.Offset + 1,
            };
            var properties = workItem.WebHook.Properties;

            if (properties != null)
            {
                webhookBody.Properties = new Dictionary <string, object>(properties);
            }
            webhookBody.Notifications = workItem.Notifications.ToArray();

            var serializer = _settings.Settings != null?JsonSerializer.Create(_settings.Settings) : JsonSerializer.CreateDefault();

            serializer.Converters.Add(new NotificationDictionarySerializer());
            return(JObject.FromObject(webhookBody, serializer));
        }
Exemple #2
0
        /// <summary>
        /// Adds a SHA 256 signature to the <paramref name="body"/> and adds it to the <paramref name="request"/> as an
        /// HTTP header to the <see cref="HttpRequestMessage"/> along with the entity body.
        /// </summary>
        /// <param name="workItem">The current <see cref="WebHookWorkItem"/>.</param>
        /// <param name="request">The request to add the signature to.</param>
        /// <param name="body">The body to sign and add to the request.</param>
        protected virtual void SignWebHookRequest(WebHookWorkItem workItem, HttpRequestMessage request, JObject body)
        {
            if (workItem == null)
            {
                throw new ArgumentNullException("workItem");
            }
            if (workItem.WebHook == null)
            {
                string msg = string.Format(CustomResource.Sender_BadWorkItem, this.GetType().Name, "WebHook");
                throw new ArgumentException(msg, "workItem");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }
            if (body == null)
            {
                throw new ArgumentNullException("body");
            }

            byte[] secret = Encoding.UTF8.GetBytes(workItem.WebHook.Secret);
            using (var hasher = new HMACSHA256(secret))
            {
                string serializedBody = body.ToString();
                request.Content = new StringContent(serializedBody, Encoding.UTF8, "application/json");

                byte[] data        = Encoding.UTF8.GetBytes(serializedBody);
                byte[] sha256      = hasher.ComputeHash(data);
                string headerValue = string.Format(CultureInfo.InvariantCulture, SignatureHeaderValueTemplate, EncodingUtilities.ToHex(sha256));
                request.Headers.Add(SignatureHeaderName, headerValue);
            }
        }
Exemple #3
0
        /// <summary>
        /// Creates a <see cref="JObject"/> used as the <see cref="HttpRequestMessage"/> entity body for a <see cref="WebHook"/>.
        /// </summary>
        /// <param name="workItem">The <see cref="WebHookWorkItem"/> representing the data to be sent.</param>
        /// <returns>An initialized <see cref="JObject"/>.</returns>
        protected virtual JObject CreateWebHookRequestBody(WebHookWorkItem workItem)
        {
            if (workItem == null)
            {
                throw new ArgumentNullException(nameof(workItem));
            }

            var body = new Dictionary <string, object>
            {
                // Set properties from work item
                [BodyIdKey]      = workItem.Id,
                [BodyAttemptKey] = workItem.Offset + 1
            };

            // Set properties from WebHook
            var properties = workItem.WebHook.Properties;

            if (properties != null)
            {
                body[BodyPropertiesKey] = new Dictionary <string, object>(properties);
            }

            // Set notifications
            body[BodyNotificationsKey] = workItem.Notifications;

            return(JObject.FromObject(body));
        }
Exemple #4
0
        protected virtual HttpRequestMessage CreateWebHookRequest(WebHookWorkItem workItem)
        {
            if (workItem == null)
            {
                throw new ArgumentNullException(nameof(workItem));
            }

            var hook = workItem.WebHook;

            // Create WebHook request
            var request = new HttpRequestMessage(HttpMethod.Post, hook.WebHookUri);

            // Fill in request body based on WebHook and work item data
            var body = CreateWebHookRequestBody(workItem);

            SignWebHookRequest(workItem, request, body);

            // Add extra request or entity headers
            foreach (var kvp in hook.Headers)
            {
                if (!request.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value))
                {
                    if (!request.Content.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value))
                    {
                        var message = string.Format(CultureInfo.CurrentCulture, CustomResources.Manager_InvalidHeader, kvp.Key, hook.Id);
                        _logger.LogError(message);
                    }
                }
            }

            return(request);
        }
Exemple #5
0
        /// <summary>
        /// Adds a SHA 256 signature to the <paramref name="body"/> and adds it to the <paramref name="request"/> as an
        /// HTTP header to the <see cref="HttpRequestMessage"/> along with the entity body.
        /// </summary>
        /// <param name="workItem">The current <see cref="WebHookWorkItem"/>.</param>
        /// <param name="request">The request to add the signature to.</param>
        /// <param name="body">The body to sign and add to the request.</param>
        protected virtual void SignWebHookRequest(WebHookWorkItem workItem, HttpRequestMessage request, JObject body)
        {
            if (workItem == null)
            {
                throw new ArgumentNullException(nameof(workItem));
            }
            if (workItem.WebHook == null)
            {
                var message = $"Invalid '{GetType().Name}' instance: '{"WebHook"}' cannot be null.";
                throw new ArgumentException(message, "workItem");
            }
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }
            if (body == null)
            {
                throw new ArgumentNullException(nameof(body));
            }

            var secret = Encoding.UTF8.GetBytes(workItem.WebHook.Secret);

            using (var hasher = new HMACSHA256(secret))
            {
                var serializedBody = body.ToString();
                request.Content = new StringContent(serializedBody, Encoding.UTF8, "application/json");

                var data        = Encoding.UTF8.GetBytes(serializedBody);
                var sha256      = hasher.ComputeHash(data);
                var headerValue = string.Format(CultureInfo.InvariantCulture, SignatureHeaderValueTemplate, EncodingUtilities.ToHex(sha256));
                request.Headers.Add(SignatureHeaderName, headerValue);
            }
        }
Exemple #6
0
        /// <summary>
        /// Launch a <see cref="WebHook"/>.
        /// </summary>
        /// <remarks>We don't let exceptions propagate out from this method as it is used by the launchers
        /// and if they see an exception they shut down.</remarks>
        private async Task LaunchWebHook(WebHookWorkItem workItem, CancellationToken cancellationToken)
        {
            await OnWebHookAttempt(workItem);

            workItem.Offset++;

            var request  = CreateWebHookRequest(workItem);
            var response = await _httpClient.SendAsync(request, cancellationToken);

            var message = string.Format(CultureInfo.CurrentCulture, CustomResources.Manager_Result, workItem.WebHook.Id, response.StatusCode, workItem.Offset);

            Logger.LogInformation(message);


            if (response.IsSuccessStatusCode)
            {
                // If we get a successful response then we are done.
                await OnWebHookSuccess(workItem);

                return;
            }
            else if (response.StatusCode == HttpStatusCode.Gone)
            {
                // If we get a 410 Gone then we are also done.
                await OnWebHookGone(workItem);

                return;
            }
            else
            {
                response.EnsureSuccessStatusCode(); // throw exception to handle via Polly
            }
        }
Exemple #7
0
        protected virtual HttpRequestMessage CreateWebHookRequest(WebHookWorkItem workItem)
        {
            if (workItem == null)
            {
                throw new ArgumentNullException(nameof(workItem));
            }

            var hook = workItem.WebHook;

            // Create WebHook request
            var request = new HttpRequestMessage(HttpMethod.Post, hook.WebHookUri);

            // Fill in request body based on WebHook and work item data
            var body = CreateWebHookRequestBody(workItem);

            SignWebHookRequest(workItem, request, body);

            AddWebHookMetadata(workItem, request);

            // Add extra request or entity headers
            foreach (var kvp in hook.Headers)
            {
                if (!request.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value))
                {
                    if (!request.Content.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value))
                    {
                        var message = string.Format("Could not add header field \'{0}\' to the WebHook request for WebHook ID \'{1}\'.", kvp.Key, hook.Id);
                        _logger.LogError(message);
                    }
                }
            }

            return(request);
        }
Exemple #8
0
        /// <summary>
        /// Dequeues available WebHooks and sends them out to each WebHook recipient.
        /// </summary>
        protected virtual async Task DequeueAndSendWebHooks(CancellationToken cancellationToken)
        {
            bool isEmpty = false;

            while (true)
            {
                try
                {
                    do
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            return;
                        }

                        // Dequeue messages from Azure queue
                        CloudQueue _queue = await _storageManager.GetCloudQueueAsync(_options.ConnectionString, AzureWebHookSender.WebHookQueue);

                        IEnumerable <CloudQueueMessage> messages = await _storageManager.GetMessagesAsync(_queue, MaxDequeuedMessages, _options.MessageTimeout);

                        // Extract the work items
                        ICollection <WebHookWorkItem> workItems = messages.Select(m =>
                        {
                            WebHookWorkItem workItem             = JsonConvert.DeserializeObject <WebHookWorkItem>(m.AsString, _serializerSettings);
                            workItem.Properties[QueueMessageKey] = m;
                            return(workItem);
                        }).ToArray();

                        if (cancellationToken.IsCancellationRequested)
                        {
                            return;
                        }

                        // Submit work items to be sent to WebHook receivers
                        if (workItems.Count > 0)
                        {
                            await _sender.SendWebHookWorkItemsAsync(workItems);
                        }
                        isEmpty = workItems.Count == 0;
                    }while (!isEmpty);
                }
                catch (Exception ex)
                {
                    CloudQueue _queue = await _storageManager.GetCloudQueueAsync(_options.ConnectionString, AzureWebHookSender.WebHookQueue);

                    string msg = string.Format(AzureStorageResource.DequeueManager_ErrorDequeueing, _queue.Name, ex.Message);
                    _logger.LogError(msg, ex);
                }

                try
                {
                    await Task.Delay(_options.Frequency, cancellationToken);
                }
                catch (OperationCanceledException oex)
                {
                    _logger.LogError(oex.Message, oex);
                    return;
                }
            }
        }
Exemple #9
0
        /// <summary>
        /// TBD
        /// </summary>
        /// <param name="webHooks"></param>
        /// <param name="notifications"></param>
        /// <returns></returns>
        public static IEnumerable <WebHookWorkItem> GetWorkItems(ICollection <WebHook> webHooks, ICollection <Notification> notifications)
        {
            var workItems = new List <WebHookWorkItem>();

            foreach (var webHook in webHooks)
            {
                ICollection <Notification> webHookNotifications;

                // Pick the notifications that apply for this particular WebHook. If we only got one notification
                // then we know that it applies to all WebHooks. Otherwise each notification may apply only to a subset.
                if (notifications.Count == 1)
                {
                    webHookNotifications = notifications;
                }
                else
                {
                    webHookNotifications = notifications.Where(n => webHook.MatchesAction(n.Action)).ToArray();
                    if (webHookNotifications.Count == 0)
                    {
                        continue;
                    }
                }

                var workItem = new WebHookWorkItem(webHook, webHookNotifications.First());
                workItems.Add(workItem);
            }
            return(workItems);
        }
Exemple #10
0
        /// <inheritdoc />
        public override async Task SendWebHookWorkItemsAsync(IEnumerable <WebHookWorkItem> workItems)
        {
            if (workItems == null)
            {
                throw new ArgumentNullException("workItems");
            }

            // Keep track of which queued messages should be deleted because processing has completed.
            List <CloudQueueMessage> deleteMessages = new List <CloudQueueMessage>();

            // Submit WebHook requests in parallel
            List <Task <HttpResponseMessage> > requestTasks = new List <Task <HttpResponseMessage> >();

            foreach (var workItem in workItems)
            {
                HttpRequestMessage request = CreateWebHookRequest(workItem);
                request.Properties[AzureWebHookDequeueManager.WorkItemKey] = workItem;

                try
                {
                    Task <HttpResponseMessage> requestTask = _parent._httpClient.SendAsync(request);
                    requestTasks.Add(requestTask);
                }
                catch (Exception ex)
                {
                    string msg = string.Format(AzureStorageResource.DequeueManager_SendFailure, request.RequestUri, ex.Message);
                    Logger.LogInformation(msg);

                    CloudQueueMessage message = GetMessage(workItem);
                    if (DiscardMessage(workItem, message))
                    {
                        deleteMessages.Add(message);
                    }
                }
            }

            // Wait for all responses and see which messages should be deleted from the queue based on the response statuses.
            HttpResponseMessage[] responses = await Task.WhenAll(requestTasks);

            foreach (HttpResponseMessage response in responses)
            {
                WebHookWorkItem workItem = response.RequestMessage.Properties[AzureWebHookDequeueManager.WorkItemKey] as WebHookWorkItem;
                string          msg      = string.Format(AzureStorageResource.DequeueManager_WebHookStatus, workItem.WebHook.Id, response.StatusCode, workItem.Offset);
                Logger.LogInformation(msg);

                // If success or 'gone' HTTP status code then we remove the message from the Azure queue.
                // If error then we leave it in the queue to be consumed once it becomes visible again or we give up
                CloudQueueMessage message = GetMessage(workItem);
                if (response.IsSuccessStatusCode || response.StatusCode == HttpStatusCode.Gone || DiscardMessage(workItem, message))
                {
                    deleteMessages.Add(message);
                }
            }

            // Delete successfully delivered messages and messages that have been attempted delivered too many times.
            CloudQueue _queue = await _parent._storageManager.GetCloudQueueAsync(_parent._options.ConnectionString, AzureWebHookSender.WebHookQueue);

            await _parent._storageManager.DeleteMessagesAsync(_queue, deleteMessages);
        }
Exemple #11
0
        /// <summary>
        /// Creates a <see cref="JObject"/> used as the <see cref="HttpRequestMessage"/> entity body for a <see cref="WebHook"/>.
        /// </summary>
        /// <param name="workItem">The <see cref="WebHookWorkItem"/> representing the data to be sent.</param>
        /// <returns>An initialized <see cref="JObject"/>.</returns>
        protected virtual JObject CreateWebHookRequestBody(WebHookWorkItem workItem)
        {
            if (workItem == null)
            {
                throw new ArgumentNullException(nameof(workItem));
            }

            return(JObject.FromObject(workItem.Notification.Payload, JsonSerializer.Create(_options.Value.SerializerSettings)));
        }
Exemple #12
0
        /// <summary>
        /// Launch a <see cref="WebHook"/>.
        /// </summary>
        /// <remarks>We don't let exceptions propagate out from this method as it is used by the launchers
        /// and if they see an exception they shut down.</remarks>
        private async Task LaunchWebHook(WebHookWorkItem workItem)
        {
            try
            {
                // Setting up and send WebHook request
                HttpRequestMessage  request  = CreateWebHookRequest(workItem);
                HttpResponseMessage response = await _httpClient.SendAsync(request);

                string msg = string.Format(CustomResource.Manager_Result, workItem.WebHook.Id, response.StatusCode, workItem.Offset);
                Logger.LogInformation(msg);

                if (response.IsSuccessStatusCode)
                {
                    // If we get a successful response then we are done.
                    await OnWebHookSuccess(workItem);

                    return;
                }
                else if (response.StatusCode == HttpStatusCode.Gone)
                {
                    // If we get a 410 Gone then we are also done.
                    await OnWebHookGone(workItem);

                    return;
                }
            }
            catch (Exception ex)
            {
                string msg = string.Format(CustomResource.Manager_WebHookFailure, workItem.Offset, workItem.WebHook.Id, ex.Message);
                Logger.LogError(msg, ex);
            }

            try
            {
                // See if we should retry the request with delay or give up
                workItem.Offset++;
                if (workItem.Offset < _launchers.Length)
                {
                    // If we are to retry then we submit the request again after a delay.
                    await OnWebHookRetry(workItem);

                    _launchers[workItem.Offset].Post(workItem);
                }
                else
                {
                    string msg = string.Format(CustomResource.Manager_GivingUp, workItem.WebHook.Id, workItem.Offset);
                    Logger.LogError(msg);
                    await OnWebHookFailure(workItem);
                }
            }
            catch (Exception ex)
            {
                string msg = string.Format(CustomResource.Manager_WebHookFailure, workItem.Offset, workItem.WebHook.Id, ex.Message);
                Logger.LogError(msg, ex);
            }
        }
Exemple #13
0
 private bool DiscardMessage(WebHookWorkItem workItem, CloudQueueMessage message)
 {
     if (message.DequeueCount >= _parent._options.MaxDeQueueCount)
     {
         string error = string.Format(AzureStorageResource.DequeueManager_GivingUp, workItem.WebHook.Id, message.DequeueCount);
         Logger.LogError(error);
         return(true);
     }
     return(false);
 }
Exemple #14
0
        /// <summary>
        /// Launch a <see cref="WebHook"/>.
        /// </summary>
        /// <remarks>We don't let exceptions propagate out from this method as it is used by the launchers
        /// and if they see an exception they shut down.</remarks>
        private async Task LaunchWebHook(WebHookWorkItem workItem)
        {
            try
            {
                // Setting up and send WebHook request
                var request  = CreateWebHookRequest(workItem);
                var response = await _httpClient.SendAsync(request);

                var message = $"WebHook '{workItem.WebHook.Id}' resulted in status code '{response.StatusCode}' on attempt '{workItem.Offset}'.";
                Logger.LogInformation(message);

                if (response.IsSuccessStatusCode)
                {
                    // If we get a successful response then we are done.
                    await OnWebHookSuccess(workItem);

                    return;
                }
                else if (response.StatusCode == HttpStatusCode.Gone)
                {
                    // If we get a 410 Gone then we are also done.
                    await OnWebHookGone(workItem);

                    return;
                }
            }
            catch (Exception ex) {
                var message = $"Failed to submit attempt {workItem.Offset} of WebHook {workItem.WebHook.Id} due to failure: {ex.Message}";
                Logger.LogError(message, ex);
            }

            try
            {
                // See if we should retry the request with delay or give up
                workItem.Offset++;
                if (workItem.Offset < _launchers.Length)
                {
                    // If we are to retry then we submit the request again after a delay.
                    await OnWebHookRetry(workItem);

                    _launchers[workItem.Offset].Post(workItem);
                }
                else
                {
                    var message = $"Giving up sending WebHook '{workItem.WebHook.Id}' after '{workItem.Offset}' attempts.";
                    Logger.LogError(message);
                    await OnWebHookFailure(workItem);
                }
            }
            catch (Exception ex)
            {
                var message = $"Failed to submit attempt {workItem.Offset} of WebHook {workItem.WebHook.Id} due to failure: {ex.Message}";
                Logger.LogError(message, ex);
            }
        }
Exemple #15
0
        private CloudQueueMessage GetMessage(WebHookWorkItem workItem)
        {
            CloudQueueMessage message = workItem != null ? workItem.Properties[AzureWebHookDequeueManager.QueueMessageKey] as CloudQueueMessage : null;

            if (message == null)
            {
                string msg = string.Format(AzureStorageResource.DequeueManager_NoProperty, AzureWebHookDequeueManager.QueueMessageKey, workItem.Id);
                Logger.LogError(msg);
                throw new InvalidOperationException(msg);
            }
            return(message);
        }
Exemple #16
0
 private async Task SendWithPolly(WebHookWorkItem workitem)
 {
     try
     {
         var policy = _policies.GetOrAdd(workitem.WebHook.WebHookUri.Host, CreatePolicy);
         await policy.ExecuteAsync((CancellationToken cancellationToken) => LaunchWebHook(workitem, cancellationToken), CancellationToken.None);
     }
     catch (Exception)
     {
         await OnWebHookFailure(workitem);
     }
 }
Exemple #17
0
        /// <summary>
        /// Creates a <see cref="JObject"/> used as the <see cref="HttpRequestMessage"/> entity body for a <see cref="WebHook"/>.
        /// </summary>
        /// <param name="workItem">The <see cref="WebHookWorkItem"/> representing the data to be sent.</param>
        /// <returns>An initialized <see cref="JObject"/>.</returns>
        protected virtual void CreateWebHookRequestBody(WebHookWorkItem workItem, StreamWriter writer)
        {
            if (workItem == null)
            {
                throw new ArgumentNullException(nameof(workItem));
            }

            // Set notifications
            var webhookBody = new WebhookBody
            {
                Id      = workItem.Id,
                Attempt = workItem.Offset + 1,
            };
            var properties = workItem.WebHook.Properties;

            if (properties != null)
            {
                webhookBody.Properties = new Dictionary <string, object>(properties);
            }
            webhookBody.Notifications = workItem.Notifications.ToArray();
            _serializer.Serialize(writer, webhookBody);
        }
Exemple #18
0
        /// <summary>
        /// Adds a SHA 256 signature to the <paramref name="body"/> and adds it to the <paramref name="request"/> as an
        /// HTTP header to the <see cref="HttpRequestMessage"/> along with the entity body.
        /// </summary>
        /// <param name="workItem">The current <see cref="WebHookWorkItem"/>.</param>
        /// <param name="request">The request to add the signature to.</param>
        /// <param name="body">The body to sign and add to the request.</param>
        protected virtual void SignWebHookRequest(WebHookWorkItem workItem, HttpRequestMessage request, Stream body)
        {
            if (workItem == null)
            {
                throw new ArgumentNullException(nameof(workItem));
            }
            if (workItem.WebHook == null)
            {
                var message = string.Format(CultureInfo.CurrentCulture, CustomResources.Sender_BadWorkItem, GetType().Name, "WebHook");
                throw new ArgumentException(message, "workItem");
            }
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }
            if (body == null)
            {
                throw new ArgumentNullException(nameof(body));
            }

            var secret = Encoding.UTF8.GetBytes(workItem.WebHook.Secret);

            using var hasher = new HMACSHA256(secret);

            var sha256      = hasher.ComputeHash(body);
            var headerValue = string.Format(CultureInfo.InvariantCulture, SignatureHeaderValueTemplate, EncodingUtilities.ToHex(sha256));

            request.Headers.Add(SignatureHeaderName, headerValue);

            body.Seek(0, SeekOrigin.Begin);

            var content = new StreamContent(body);

            content.Headers.ContentType = ApplicationJson;
            request.Content             = content;
        }
Exemple #19
0
        protected virtual async Task <HttpRequestMessage> CreateWebHookRequestAsync(WebHookWorkItem workItem)
        {
            if (workItem == null)
            {
                throw new ArgumentNullException(nameof(workItem));
            }

            var hook = workItem.WebHook;

            // Create WebHook request
            var request = new HttpRequestMessage(HttpMethod.Post, hook.WebHookUri);

            var ms     = new MemoryStream();
            var writer = new StreamWriter(ms);

            CreateWebHookRequestBody(workItem, writer);
            await writer.FlushAsync();

            ms.Seek(0, SeekOrigin.Begin);
            SignWebHookRequest(workItem, request, ms);

            // Add extra request or entity headers
            foreach (var kvp in hook.Headers)
            {
                if (!request.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value))
                {
                    if (!request.Content.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value))
                    {
                        var message = string.Format(CultureInfo.CurrentCulture, CustomResources.Manager_InvalidHeader, kvp.Key, hook.Id);
                        _logger.LogError(message);
                    }
                }
            }

            return(request);
        }
Exemple #20
0
        private async Task DelayedLaunchWebHook(WebHookWorkItem item, TimeSpan delay)
        {
            await Task.Delay(delay);

            await LaunchWebHook(item);
        }
Exemple #21
0
 /// <summary>
 /// If delivery of a WebHook results in a 410 Gone HTTP status code, then <see cref="OnWebHookGone"/>
 /// is called enabling additional post-processing.
 /// </summary>
 /// <param name="workItem">The current <see cref="WebHookWorkItem"/>.</param>
 protected virtual Task OnWebHookGone(WebHookWorkItem workItem)
 {
     return(Task.FromResult(true));
 }
Exemple #22
0
 protected virtual Task OnWebHookAttempt(WebHookWorkItem workitem)
 {
     return(Task.CompletedTask);
 }
Exemple #23
0
 private void AddWebHookMetadata(WebHookWorkItem workItem, HttpRequestMessage request)
 {
     request.Headers.Add(HeaderIdKey, workItem.Id);
     request.Headers.Add(HeaderAttemptKey, (workItem.Offset + 1).ToString());
     request.Headers.Add(HeaderNotificationKey, workItem.Notification.Action);
 }
Exemple #24
0
 /// <summary>
 /// If delivery of a WebHook results in a 410 Gone HTTP status code, then <see cref="OnWebHookGone"/>
 /// is called enabling additional post-processing.
 /// </summary>
 /// <param name="workItem">The current <see cref="WebHookWorkItem"/>.</param>
 protected virtual Task OnWebHookGone(WebHookWorkItem workItem)
 {
     return(Task.CompletedTask);
 }
Exemple #25
0
 /// <summary>
 /// If delivery of a WebHook is successful, i.e. a 2xx HTTP status code is received,
 /// then <see cref="OnWebHookSuccess"/> is called enabling additional post-processing.
 /// </summary>
 /// <param name="workItem">The current <see cref="WebHookWorkItem"/>.</param>
 protected virtual Task OnWebHookSuccess(WebHookWorkItem workItem) => Task.CompletedTask;
Exemple #26
0
 /// <summary>
 /// If delivery of a WebHook results in a 410 Gone HTTP status code, then <see cref="OnWebHookGone"/>
 /// is called enabling additional post-processing.
 /// </summary>
 /// <param name="workItem">The current <see cref="WebHookWorkItem"/>.</param>
 protected virtual Task OnWebHookGone(WebHookWorkItem workItem) => Task.CompletedTask;
Exemple #27
0
        /// <summary>
        /// Launch a <see cref="WebHook"/>.
        /// </summary>
        /// <remarks>We don't let exceptions propagate out from this method as it is used by the launchers
        /// and if they see an exception they shut down.</remarks>
        private async Task LaunchWebHook(WebHookWorkItem workItem)
        {
            var policy = _policyContainer.GetPolicyFor(workItem.WebHook);

            try
            {
                policy.AcquireUse();

                // Setting up and send WebHook request
                using var request = await CreateWebHookRequestAsync(workItem);

                using var response = await _httpClient.SendAsync(request);

                var message = string.Format(CultureInfo.CurrentCulture, CustomResources.Manager_Result, workItem.WebHook.Id, response.StatusCode, workItem.Offset);
                Logger.LogInformation(message);

                if (response.IsSuccessStatusCode)
                {
                    policy.Success();
                    // If we get a successful response then we are done.
                    await OnWebHookSuccess(workItem);

                    return;
                }
                else if (response.StatusCode == HttpStatusCode.Gone)
                {
                    // If we get a 410 Gone then we are also done.
                    await OnWebHookGone(workItem);

                    return;
                }
            }
            catch (CircuitBreakerException)
            {
                var message = string.Format(CultureInfo.CurrentCulture, CustomResources.Manager_GivingUp, workItem.WebHook.Id, workItem.Offset);
                Logger.LogInformation(message);
                await OnWebHookFailure(workItem);

                return;
            }
            catch (Exception ex)
            {
                var message = string.Format(CultureInfo.CurrentCulture, CustomResources.Manager_WebHookFailure, workItem.Offset, workItem.WebHook.Id, ex.Message);
                Logger.LogInformation(message, ex);
            }

            try
            {
                // See if we should retry the request with delay or give up
                workItem.Offset++;
                policy.Failure();
                if (workItem.Offset < _launchers.Length)
                {
                    // If we are to retry then we submit the request again after a delay.
                    await OnWebHookRetry(workItem);

                    _launchers[workItem.Offset].Post(workItem);
                }
                else
                {
                    var message = string.Format(CultureInfo.CurrentCulture, CustomResources.Manager_GivingUp, workItem.WebHook.Id, workItem.Offset);
                    Logger.LogInformation(message);
                    await OnWebHookFailure(workItem);
                }
            }
            catch (Exception ex)
            {
                var message = string.Format(CultureInfo.CurrentCulture, CustomResources.Manager_WebHookFailure, workItem.Offset, workItem.WebHook.Id, ex.Message);
                Logger.LogInformation(message, ex);
            }
        }