Cody Robibero 2 weeks ago committed by GitHub
commit 44503ce56b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -10,8 +10,12 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Mime;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using Emby.Naming.Common;
using Emby.Photos;
@ -37,6 +41,7 @@ using Emby.Server.Implementations.Updates;
using Jellyfin.Api.Helpers;
using Jellyfin.Drawing;
using Jellyfin.MediaEncoding.Hls.Playlist;
using Jellyfin.Networking.HappyEyeballs;
using Jellyfin.Networking.Manager;
using Jellyfin.Networking.Udp;
using Jellyfin.Server.Implementations;
@ -281,6 +286,118 @@ namespace Emby.Server.Implementations
.Replace(appPaths.InternalMetadataPath, appPaths.VirtualInternalMetadataPath, StringComparison.OrdinalIgnoreCase);
}
/// <inheritdoc />
public void AddPluginHttpClient<T>(
IServiceCollection serviceCollection,
string basedOnClientName,
(string Name, string[] Values)[] customHeaders)
where T : BasePlugin
{
ArgumentNullException.ThrowIfNull(serviceCollection);
ArgumentException.ThrowIfNullOrEmpty(basedOnClientName);
var pluginType = typeof(T);
if (string.Equals(NamedClient.Default, basedOnClientName, StringComparison.Ordinal)
|| string.Equals(NamedClient.DirectIp, basedOnClientName, StringComparison.Ordinal))
{
AddHttpClient(serviceCollection, basedOnClientName, customHeaders, pluginType);
}
else
{
throw new InvalidOperationException($"Unknown HttpClient name '{basedOnClientName}' for plugin '{pluginType.Name}'");
}
}
/// <summary>
/// Add HttpClient.
/// Intended for internal use only.
/// </summary>
/// <param name="serviceCollection">The service collection.</param>
/// <param name="basedOnClientName">The name of the HttpClient to base this client on.</param>
/// <param name="customHeaders">The custom headers to use.</param>
/// <param name="pluginType">The type of plugin adding the HttpClient.</param>
public void AddHttpClient(
IServiceCollection serviceCollection,
string basedOnClientName,
(string Name, string[] Values)[] customHeaders = null,
Type pluginType = null)
{
var clientName = basedOnClientName;
ProductInfoHeaderValue pluginHeader = null;
if (pluginType is not null)
{
var assembly = pluginType.Assembly.GetName();
clientName = pluginType.Name;
pluginHeader = new ProductInfoHeaderValue(
clientName.Replace(' ', '-').Replace('.', '-'),
assembly.Version!.ToString(3));
}
if (string.Equals(NamedClient.Default, basedOnClientName, StringComparison.Ordinal))
{
serviceCollection.AddHttpClient(clientName, ConfigureClient)
.ConfigurePrimaryHttpMessageHandler(EyeballsHttpClientHandlerDelegate);
}
else if (string.Equals(NamedClient.DirectIp, basedOnClientName, StringComparison.Ordinal))
{
serviceCollection.AddHttpClient(clientName, ConfigureClient)
.ConfigurePrimaryHttpMessageHandler(DefaultHttpClientHandlerDelegate);
}
else
{
throw new InvalidOperationException($"Unknown HttpClient name '{basedOnClientName}'");
}
Logger.LogInformation("Added HttpClient '{ClientName}'", clientName);
return;
void ConfigureClient(HttpClient c)
{
var productHeader = new ProductInfoHeaderValue(Name.Replace(' ', '-'), ApplicationVersionString);
var contactHeader = new ProductInfoHeaderValue($"({ApplicationUserAgentAddress})");
var acceptJsonHeader = new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json, 1.0);
var acceptXmlHeader = new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Xml, 0.9);
var acceptAnyHeader = new MediaTypeWithQualityHeaderValue("*/*", 0.8);
if (customHeaders is null)
{
c.DefaultRequestHeaders.UserAgent.Add(productHeader);
c.DefaultRequestHeaders.UserAgent.Add(contactHeader);
if (pluginHeader is not null)
{
c.DefaultRequestHeaders.UserAgent.Add(pluginHeader);
}
c.DefaultRequestHeaders.Accept.Add(acceptJsonHeader);
c.DefaultRequestHeaders.Accept.Add(acceptXmlHeader);
c.DefaultRequestHeaders.Accept.Add(acceptAnyHeader);
}
else
{
c.DefaultRequestHeaders.Clear();
foreach (var (name, values) in customHeaders)
{
c.DefaultRequestHeaders.Add(name, values);
}
}
}
static HttpMessageHandler DefaultHttpClientHandlerDelegate(IServiceProvider s) => new SocketsHttpHandler
{
AutomaticDecompression = DecompressionMethods.All,
RequestHeaderEncodingSelector = (_, _) => Encoding.UTF8
};
static HttpMessageHandler EyeballsHttpClientHandlerDelegate(IServiceProvider s) => new SocketsHttpHandler
{
AutomaticDecompression = DecompressionMethods.All,
RequestHeaderEncodingSelector = (_, _) => Encoding.UTF8,
ConnectCallback = HttpClientExtension.OnConnect
};
}
/// <summary>
/// Creates the instance safe.
/// </summary>

@ -26,7 +26,6 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Trickplay;
using MediaBrowser.Model.Activity;
using MediaBrowser.Providers.Lyric;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

@ -1,16 +1,10 @@
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Mime;
using System.Text;
using Emby.Server.Implementations.EntryPoints;
using Jellyfin.Api.Middleware;
using Jellyfin.LiveTv.Extensions;
using Jellyfin.LiveTv.Recordings;
using Jellyfin.MediaEncoding.Hls.Extensions;
using Jellyfin.Networking;
using Jellyfin.Networking.HappyEyeballs;
using Jellyfin.Server.Extensions;
using Jellyfin.Server.HealthChecks;
using Jellyfin.Server.Implementations;
@ -75,51 +69,8 @@ namespace Jellyfin.Server
services.AddJellyfinApiAuthorization();
var productHeader = new ProductInfoHeaderValue(
_serverApplicationHost.Name.Replace(' ', '-'),
_serverApplicationHost.ApplicationVersionString);
var acceptJsonHeader = new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json, 1.0);
var acceptXmlHeader = new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Xml, 0.9);
var acceptAnyHeader = new MediaTypeWithQualityHeaderValue("*/*", 0.8);
Func<IServiceProvider, HttpMessageHandler> eyeballsHttpClientHandlerDelegate = (_) => new SocketsHttpHandler()
{
AutomaticDecompression = DecompressionMethods.All,
RequestHeaderEncodingSelector = (_, _) => Encoding.UTF8,
ConnectCallback = HttpClientExtension.OnConnect
};
Func<IServiceProvider, HttpMessageHandler> defaultHttpClientHandlerDelegate = (_) => new SocketsHttpHandler()
{
AutomaticDecompression = DecompressionMethods.All,
RequestHeaderEncodingSelector = (_, _) => Encoding.UTF8
};
services.AddHttpClient(NamedClient.Default, c =>
{
c.DefaultRequestHeaders.UserAgent.Add(productHeader);
c.DefaultRequestHeaders.Accept.Add(acceptJsonHeader);
c.DefaultRequestHeaders.Accept.Add(acceptXmlHeader);
c.DefaultRequestHeaders.Accept.Add(acceptAnyHeader);
})
.ConfigurePrimaryHttpMessageHandler(eyeballsHttpClientHandlerDelegate);
services.AddHttpClient(NamedClient.MusicBrainz, c =>
{
c.DefaultRequestHeaders.UserAgent.Add(productHeader);
c.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue($"({_serverApplicationHost.ApplicationUserAgentAddress})"));
c.DefaultRequestHeaders.Accept.Add(acceptXmlHeader);
c.DefaultRequestHeaders.Accept.Add(acceptAnyHeader);
})
.ConfigurePrimaryHttpMessageHandler(eyeballsHttpClientHandlerDelegate);
services.AddHttpClient(NamedClient.DirectIp, c =>
{
c.DefaultRequestHeaders.UserAgent.Add(productHeader);
c.DefaultRequestHeaders.Accept.Add(acceptJsonHeader);
c.DefaultRequestHeaders.Accept.Add(acceptXmlHeader);
c.DefaultRequestHeaders.Accept.Add(acceptAnyHeader);
})
.ConfigurePrimaryHttpMessageHandler(defaultHttpClientHandlerDelegate);
_serverApplicationHost.AddHttpClient(services, NamedClient.Default);
_serverApplicationHost.AddHttpClient(services, NamedClient.DirectIp);
services.AddHealthChecks()
.AddCheck<DbContextFactoryHealthCheck<JellyfinDbContext>>(nameof(JellyfinDbContext));

@ -0,0 +1,22 @@
using System;
using System.Net.Http;
using MediaBrowser.Common.Plugins;
namespace MediaBrowser.Common.Extensions;
/// <summary>
/// Extensions for <see cref="IServiceProvider"/>.
/// </summary>
public static class HttpClientFactoryExtensions
{
/// <summary>
/// Get a plugin-configured HttpClient.
/// This requires calling <c>AddHttpClient{T}</c> during <c>RegisterServices</c>.
/// </summary>
/// <param name="httpClientFactory">The http client factory.</param>
/// <typeparam name="T">The type of plugin.</typeparam>
/// <returns>The HttpClient.</returns>
public static HttpClient GetPluginHttpClient<T>(this IHttpClientFactory httpClientFactory)
where T : BasePlugin
=> httpClientFactory.CreateClient(typeof(T).Name);
}

@ -1,28 +1,17 @@
namespace MediaBrowser.Common.Net
namespace MediaBrowser.Common.Net;
/// <summary>
/// Registered http client names.
/// </summary>
public static class NamedClient
{
/// <summary>
/// Registered http client names.
/// Gets the value for the default named http client which implements happy eyeballs.
/// </summary>
public static class NamedClient
{
/// <summary>
/// Gets the value for the default named http client which implements happy eyeballs.
/// </summary>
public const string Default = nameof(Default);
/// <summary>
/// Gets the value for the MusicBrainz named http client.
/// </summary>
public const string MusicBrainz = nameof(MusicBrainz);
/// <summary>
/// Gets the value for the DLNA named http client.
/// </summary>
public const string Dlna = nameof(Dlna);
public const string Default = nameof(Default);
/// <summary>
/// Non happy eyeballs implementation.
/// </summary>
public const string DirectIp = nameof(DirectIp);
}
/// <summary>
/// Non happy eyeballs implementation.
/// </summary>
public const string DirectIp = nameof(DirectIp);
}

@ -4,7 +4,10 @@
using System.Net;
using MediaBrowser.Common;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Plugins;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
namespace MediaBrowser.Controller
{
@ -86,5 +89,18 @@ namespace MediaBrowser.Controller
string ExpandVirtualPath(string path);
string ReverseVirtualPath(string path);
/// <summary>
/// Add a plugin HttpClient.
/// </summary>
/// <param name="serviceCollection">The service collection to add to.</param>
/// <param name="basedOnClientName">The name of the HttpClient to base config on.</param>
/// <param name="customHeaders">The custom headers to use. If provided no other headers will be added.</param>
/// <typeparam name="T">The plugin type.</typeparam>
void AddPluginHttpClient<T>(
IServiceCollection serviceCollection,
string basedOnClientName = nameof(NamedClient.Default),
(string Name, string[] Values)[] customHeaders = null)
where T : BasePlugin;
}
}

Loading…
Cancel
Save