refactor: Upgrade Flurl to v4.0.0

Required refactoring the way before/after/redirect handlers were
implemented.
pull/231/head
Robert Dailey 11 months ago
parent 7d0d360abb
commit 258949c0aa

@ -11,7 +11,7 @@
<PackageVersion Include="CliWrap" Version="3.6.4" />
<PackageVersion Include="FluentValidation" Version="11.8.1" />
<PackageVersion Include="Flurl" Version="3.0.7" />
<PackageVersion Include="Flurl.Http" Version="4.0.0-pre6" />
<PackageVersion Include="Flurl.Http" Version="4.0.0" />
<PackageVersion Include="GitVersion.MsBuild" Version="5.12.0" />
<PackageVersion Include="JetBrains.Annotations" Version="2023.3.0" />
<PackageVersion Include="MudBlazor" Version="6.11.0" />

@ -2,6 +2,7 @@ using Autofac;
using Flurl.Http.Configuration;
using Recyclarr.ServarrApi.CustomFormat;
using Recyclarr.ServarrApi.Http;
using Recyclarr.ServarrApi.Http.Servarr;
using Recyclarr.ServarrApi.MediaNaming;
using Recyclarr.ServarrApi.QualityDefinition;
using Recyclarr.ServarrApi.QualityProfile;
@ -26,5 +27,11 @@ public class ApiServicesAutofacModule : Module
builder.RegisterType<QualityDefinitionApiService>().As<IQualityDefinitionApiService>();
builder.RegisterType<MediaNamingApiService>().As<IMediaNamingApiService>();
builder.RegisterType<SonarrTagApiService>().As<ISonarrTagApiService>();
builder.RegisterTypes(
typeof(FlurlAfterCallHandler),
typeof(FlurlBeforeCallHandler),
typeof(FlurlRedirectHandler))
.As<FlurlSpecificEventHandler>();
}
}

@ -1,6 +1,6 @@
using Flurl.Http;
using Recyclarr.Config.Models;
using Recyclarr.ServarrApi.Http;
using Recyclarr.ServarrApi.Http.Servarr;
using Recyclarr.TrashGuide.CustomFormat;
namespace Recyclarr.ServarrApi.CustomFormat;

@ -1,49 +1,12 @@
using System.Text.Json;
using Flurl;
using Flurl.Http.Configuration;
using Serilog;
namespace Recyclarr.ServarrApi.Http;
public static class FlurlLogging
{
public static void SetupLogging(FlurlHttpSettings settings, ILogger log, Func<Url, Url>? urlInterceptor = null)
{
urlInterceptor ??= SanitizeUrl;
settings.BeforeCall = call =>
{
var url = urlInterceptor(call.Request.Url.Clone());
log.Debug("HTTP Request: {Method} {Url}", call.HttpRequestMessage.Method, url);
LogBody(log, url, "Request", call.HttpRequestMessage.Method, call.RequestBody);
};
settings.AfterCallAsync = async call =>
{
var statusCode = call.Response?.StatusCode.ToString() ?? "(No response)";
var url = urlInterceptor(call.Request.Url.Clone());
log.Debug("HTTP Response: {Status} {Method} {Url}", statusCode, call.HttpRequestMessage.Method, url);
var content = call.Response?.ResponseMessage.Content;
if (content is not null)
{
LogBody(log, url, "Response", call.HttpRequestMessage.Method, await content.ReadAsStringAsync());
}
};
settings.OnRedirect = call =>
{
log.Warning("HTTP Redirect received; this indicates a problem with your URL and/or reverse proxy: {Url}",
urlInterceptor(call.Redirect.Url));
// Must follow redirect because we want an exception to be thrown eventually. If it is set to false, HTTP
// communication stops and existing methods will return nothing / null. This messes with Observable
// pipelines (which normally either expect a response object or an exception)
call.Redirect.Follow = true;
};
}
private static void LogBody(ILogger log, Url url, string direction, HttpMethod method, string? body)
public static void LogBody(ILogger log, Url url, string direction, HttpMethod method, string? body)
{
if (string.IsNullOrEmpty(body))
{

@ -0,0 +1,11 @@
using System.Diagnostics.CodeAnalysis;
using Flurl.Http;
namespace Recyclarr.ServarrApi.Http;
[SuppressMessage("Naming", "CA1711:Identifiers should not have incorrect suffix", Justification =
"Naming convention borrowed from and determined by Flurl")]
public abstract class FlurlSpecificEventHandler : FlurlEventHandler
{
public abstract FlurlEventType EventType { get; }
}

@ -1,7 +1,7 @@
using Flurl.Http;
using Recyclarr.Config.Models;
namespace Recyclarr.ServarrApi.Http;
namespace Recyclarr.ServarrApi.Http.Servarr;
public interface IServarrRequestBuilder
{

@ -0,0 +1,55 @@
using Flurl.Http;
using JetBrains.Annotations;
using Serilog;
namespace Recyclarr.ServarrApi.Http.Servarr;
[UsedImplicitly]
public class FlurlBeforeCallHandler(ILogger log) : FlurlSpecificEventHandler
{
public override FlurlEventType EventType => FlurlEventType.BeforeCall;
public override void Handle(FlurlEventType eventType, FlurlCall call)
{
var url = FlurlLogging.SanitizeUrl(call.Request.Url.Clone());
log.Debug("HTTP Request: {Method} {Url}", call.HttpRequestMessage.Method, url);
FlurlLogging.LogBody(log, url, "Request", call.HttpRequestMessage.Method, call.RequestBody);
}
}
[UsedImplicitly]
public class FlurlAfterCallHandler(ILogger log) : FlurlSpecificEventHandler
{
public override FlurlEventType EventType => FlurlEventType.AfterCall;
public override async Task HandleAsync(FlurlEventType eventType, FlurlCall call)
{
var statusCode = call.Response?.StatusCode.ToString() ?? "(No response)";
var url = FlurlLogging.SanitizeUrl(call.Request.Url.Clone());
log.Debug("HTTP Response: {Status} {Method} {Url}", statusCode, call.HttpRequestMessage.Method, url);
var content = call.Response?.ResponseMessage.Content;
if (content is not null)
{
FlurlLogging.LogBody(log, url, "Response", call.HttpRequestMessage.Method,
await content.ReadAsStringAsync());
}
}
}
[UsedImplicitly]
public class FlurlRedirectHandler(ILogger log) : FlurlSpecificEventHandler
{
public override FlurlEventType EventType => FlurlEventType.OnRedirect;
public override void Handle(FlurlEventType eventType, FlurlCall call)
{
log.Warning("HTTP Redirect received; this indicates a problem with your URL and/or reverse proxy: {Url}",
FlurlLogging.SanitizeUrl(call.Redirect.Url));
// Must follow redirect because we want an exception to be thrown eventually. If it is set to false, HTTP
// communication stops and existing methods will return nothing / null. This messes with Observable
// pipelines (which normally either expect a response object or an exception)
call.Redirect.Follow = true;
}
}

@ -7,12 +7,13 @@ using Recyclarr.Json;
using Recyclarr.Settings;
using Serilog;
namespace Recyclarr.ServarrApi.Http;
namespace Recyclarr.ServarrApi.Http.Servarr;
public class ServarrRequestBuilder(
ILogger log,
IFlurlClientCache clientCache,
ISettingsProvider settingsProvider)
ISettingsProvider settingsProvider,
IEnumerable<FlurlSpecificEventHandler> eventHandlers)
: IServarrRequestBuilder
{
public IFlurlRequest Request(IServiceConfiguration config, params object[] path)
@ -30,10 +31,14 @@ public class ServarrRequestBuilder(
[SuppressMessage("Security", "CA5359:Do Not Disable Certificate Validation")]
private void Configure(IFlurlClientBuilder builder)
{
foreach (var handler in eventHandlers.Select(x => (x.EventType, x)))
{
builder.EventHandlers.Add(handler);
}
builder.WithSettings(settings =>
{
settings.JsonSerializer = new DefaultJsonSerializer(GlobalJsonSerializerSettings.Services);
FlurlLogging.SetupLogging(settings, log);
});
builder.ConfigureInnerHandler(handler =>

@ -1,7 +1,7 @@
using Flurl.Http;
using Recyclarr.Common;
using Recyclarr.Config.Models;
using Recyclarr.ServarrApi.Http;
using Recyclarr.ServarrApi.Http.Servarr;
namespace Recyclarr.ServarrApi.MediaNaming;

@ -1,6 +1,6 @@
using Flurl.Http;
using Recyclarr.Config.Models;
using Recyclarr.ServarrApi.Http;
using Recyclarr.ServarrApi.Http.Servarr;
namespace Recyclarr.ServarrApi.QualityDefinition;

@ -1,6 +1,6 @@
using Flurl.Http;
using Recyclarr.Config.Models;
using Recyclarr.ServarrApi.Http;
using Recyclarr.ServarrApi.Http.Servarr;
namespace Recyclarr.ServarrApi.QualityProfile;

@ -1,6 +1,6 @@
using Flurl.Http;
using Recyclarr.Config.Models;
using Recyclarr.ServarrApi.Http;
using Recyclarr.ServarrApi.Http.Servarr;
namespace Recyclarr.ServarrApi.ReleaseProfile;

@ -1,6 +1,6 @@
using Flurl.Http;
using Recyclarr.Config.Models;
using Recyclarr.ServarrApi.Http;
using Recyclarr.ServarrApi.Http.Servarr;
namespace Recyclarr.ServarrApi.System;

@ -1,6 +1,6 @@
using Flurl.Http;
using Recyclarr.Config.Models;
using Recyclarr.ServarrApi.Http;
using Recyclarr.ServarrApi.Http.Servarr;
namespace Recyclarr.ServarrApi.Tag;

Loading…
Cancel
Save