Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/Umbraco.Core/Constants-HttpClients.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,18 @@ public static class HttpClients
/// Name for http client which ignores certificate errors.
/// </summary>
public const string IgnoreCertificateErrors = "Umbraco:HttpClients:IgnoreCertificateErrors";

/// <summary>
/// Name for http client which sends webhook requests.
/// </summary>
public const string WebhookFiring = "Umbraco:HttpClients:WebhookFiring";

public static class Headers
{
/// <summary>
/// User agent name for the product name.
/// </summary>
public const string UserAgentProductName = "Umbraco-Cms";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public virtual string DownloadResponse(string url)
if (_httpClient == null)
{
_httpClient = new HttpClient();
_httpClient.DefaultRequestHeaders.UserAgent.TryParseAdd("Umbraco-CMS");
_httpClient.DefaultRequestHeaders.UserAgent.TryParseAdd(Constants.HttpClients.Headers.UserAgentProductName);
}

using (var request = new HttpRequestMessage(HttpMethod.Get, url))
Expand Down
42 changes: 26 additions & 16 deletions src/Umbraco.Infrastructure/BackgroundJobs/Jobs/WebhookFiring.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Serialization;
using Umbraco.Cms.Core.Services;

namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
Expand All @@ -15,11 +14,11 @@ public class WebhookFiring : IRecurringBackgroundJob
{
private readonly ILogger<WebhookFiring> _logger;
private readonly IWebhookRequestService _webhookRequestService;
private readonly IJsonSerializer _jsonSerializer;
private readonly IWebhookLogFactory _webhookLogFactory;
private readonly IWebhookLogService _webhookLogService;
private readonly IWebhookService _webHookService;
private readonly ICoreScopeProvider _coreScopeProvider;
private readonly IHttpClientFactory _httpClientFactory;
private WebhookSettings _webhookSettings;

public TimeSpan Period => _webhookSettings.Period;
Expand All @@ -32,20 +31,20 @@ public event EventHandler PeriodChanged { add { } remove { } }
public WebhookFiring(
ILogger<WebhookFiring> logger,
IWebhookRequestService webhookRequestService,
IJsonSerializer jsonSerializer,
IWebhookLogFactory webhookLogFactory,
IWebhookLogService webhookLogService,
IWebhookService webHookService,
IOptionsMonitor<WebhookSettings> webhookSettings,
ICoreScopeProvider coreScopeProvider)
ICoreScopeProvider coreScopeProvider,
IHttpClientFactory httpClientFactory)
{
_logger = logger;
_webhookRequestService = webhookRequestService;
_jsonSerializer = jsonSerializer;
_webhookLogFactory = webhookLogFactory;
_webhookLogService = webhookLogService;
_webHookService = webHookService;
_coreScopeProvider = coreScopeProvider;
_httpClientFactory = httpClientFactory;
_webhookSettings = webhookSettings.CurrentValue;
webhookSettings.OnChange(x => _webhookSettings = x);
}
Expand All @@ -72,8 +71,7 @@ await Task.WhenAll(requests.Select(request =>
return;
}

HttpResponseMessage? response = await SendRequestAsync(webhook, request.EventAlias, request.RequestObject, request.RetryCount, CancellationToken.None);

using HttpResponseMessage? response = await SendRequestAsync(webhook, request.EventAlias, request.RequestObject, request.RetryCount, CancellationToken.None);
if ((response?.IsSuccessStatusCode ?? false) || request.RetryCount >= _webhookSettings.MaximumRetries)
{
await _webhookRequestService.DeleteAsync(request);
Expand All @@ -90,24 +88,36 @@ await Task.WhenAll(requests.Select(request =>

private async Task<HttpResponseMessage?> SendRequestAsync(IWebhook webhook, string eventName, string? serializedObject, int retryCount, CancellationToken cancellationToken)
{
using var httpClient = new HttpClient();

var stringContent = new StringContent(serializedObject ?? string.Empty, Encoding.UTF8, MediaTypeNames.Application.Json);
stringContent.Headers.TryAddWithoutValidation("Umb-Webhook-Event", eventName);
using HttpClient httpClient = _httpClientFactory.CreateClient(Constants.HttpClients.WebhookFiring);

foreach (KeyValuePair<string, string> header in webhook.Headers)
using var request = new HttpRequestMessage(HttpMethod.Post, webhook.Url)
{
stringContent.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
Version = httpClient.DefaultRequestVersion,
VersionPolicy = httpClient.DefaultVersionPolicy,
};

HttpResponseMessage? response = null;
try
{
response = await httpClient.PostAsync(webhook.Url, stringContent, cancellationToken);
// Add headers
request.Headers.Add("Umb-Webhook-Event", eventName);
request.Headers.Add("Umb-Webhook-RetryCount", retryCount.ToString());
request.Headers.Add("Umb-Webhook-Date", DateTime.Now.ToString("R"));

foreach (KeyValuePair<string, string> header in webhook.Headers)
{
request.Headers.Add(header.Key, header.Value);
}

// Set content
request.Content = new StringContent(serializedObject ?? string.Empty, Encoding.UTF8, MediaTypeNames.Application.Json);

// Send request
response = await httpClient.SendAsync(request, cancellationToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while sending webhook request for webhook {WebhookKey}.", webhook);
_logger.LogError(ex, "Error while sending webhook request for webhook {WebhookKey}.", webhook.Key);
}

var webhookResponseModel = new WebhookResponseModel
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Data.Common;
using System.Net.Http.Headers;
using System.Reflection;
using Dazinator.Extensions.FileProviders.GlobPatternFilter;
using Microsoft.AspNetCore.Builder;
Expand All @@ -10,7 +11,6 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Serilog.Extensions.Logging;
Expand All @@ -23,6 +23,7 @@
using Umbraco.Cms.Core.Blocks;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.Configuration;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Diagnostics;
Expand Down Expand Up @@ -261,6 +262,11 @@ private static IUmbracoBuilder AddHttpClients(this IUmbracoBuilder builder)
ServerCertificateCustomValidationCallback =
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator,
});
builder.Services.AddHttpClient(Constants.HttpClients.WebhookFiring, (services, client) =>
{
var productVersion = services.GetRequiredService<IUmbracoVersion>().SemanticVersion.ToSemanticStringWithoutBuild();
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(Constants.HttpClients.Headers.UserAgentProductName, productVersion));
});
return builder;
}

Expand Down