diff options
| author | Nyanmisaka <nst799610810@gmail.com> | 2020-09-04 02:55:57 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-09-04 02:55:57 +0800 |
| commit | 4cb0a57e4645aba8e5e65c7d086091b9161c6c09 (patch) | |
| tree | 25e77817485d70cac8ec3e11a785b08b69d0c60b /MediaBrowser.Common | |
| parent | 54349fc94597824714f623b8c31583fc044274aa (diff) | |
| parent | 53703566b5e1239bbab308031d94df34a4d168aa (diff) | |
Merge branch 'master' into tonemap
Diffstat (limited to 'MediaBrowser.Common')
13 files changed, 211 insertions, 216 deletions
diff --git a/MediaBrowser.Common/Extensions/HttpContextExtensions.cs b/MediaBrowser.Common/Extensions/HttpContextExtensions.cs index d746207c7..e0cf3f9ac 100644 --- a/MediaBrowser.Common/Extensions/HttpContextExtensions.cs +++ b/MediaBrowser.Common/Extensions/HttpContextExtensions.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Model.Services; +using System.Net; +using MediaBrowser.Common.Net; using Microsoft.AspNetCore.Http; namespace MediaBrowser.Common.Extensions @@ -8,26 +9,55 @@ namespace MediaBrowser.Common.Extensions /// </summary> public static class HttpContextExtensions { - private const string ServiceStackRequest = "ServiceStackRequest"; - /// <summary> - /// Set the ServiceStack request. + /// Checks the origin of the HTTP request. /// </summary> - /// <param name="httpContext">The HttpContext instance.</param> - /// <param name="request">The service stack request instance.</param> - public static void SetServiceStackRequest(this HttpContext httpContext, IRequest request) + /// <param name="request">The incoming HTTP request.</param> + /// <returns><c>true</c> if the request is coming from LAN, <c>false</c> otherwise.</returns> + public static bool IsLocal(this HttpRequest request) { - httpContext.Items[ServiceStackRequest] = request; + return (request.HttpContext.Connection.LocalIpAddress == null + && request.HttpContext.Connection.RemoteIpAddress == null) + || request.HttpContext.Connection.LocalIpAddress.Equals(request.HttpContext.Connection.RemoteIpAddress); } /// <summary> - /// Get the ServiceStack request. + /// Extracts the remote IP address of the caller of the HTTP request. /// </summary> - /// <param name="httpContext">The HttpContext instance.</param> - /// <returns>The service stack request instance.</returns> - public static IRequest GetServiceStackRequest(this HttpContext httpContext) + /// <param name="request">The HTTP request.</param> + /// <returns>The remote caller IP address.</returns> + public static string RemoteIp(this HttpRequest request) { - return (IRequest)httpContext.Items[ServiceStackRequest]; + var cachedRemoteIp = request.HttpContext.Items["RemoteIp"]?.ToString(); + if (!string.IsNullOrEmpty(cachedRemoteIp)) + { + return cachedRemoteIp; + } + + IPAddress ip; + + // "Real" remote ip might be in X-Forwarded-For of X-Real-Ip + // (if the server is behind a reverse proxy for example) + if (!IPAddress.TryParse(request.Headers[CustomHeaderNames.XForwardedFor].ToString(), out ip)) + { + if (!IPAddress.TryParse(request.Headers[CustomHeaderNames.XRealIP].ToString(), out ip)) + { + ip = request.HttpContext.Connection.RemoteIpAddress; + + // Default to the loopback address if no RemoteIpAddress is specified (i.e. during integration tests) + ip ??= IPAddress.Loopback; + } + } + + if (ip.IsIPv4MappedToIPv6) + { + ip = ip.MapToIPv4(); + } + + var normalizedIp = ip.ToString(); + + request.HttpContext.Items["RemoteIp"] = normalizedIp; + return normalizedIp; } } } diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs index e8d9282e4..849037ac4 100644 --- a/MediaBrowser.Common/IApplicationHost.cs +++ b/MediaBrowser.Common/IApplicationHost.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Reflection; using System.Threading.Tasks; using MediaBrowser.Common.Plugins; using Microsoft.Extensions.DependencyInjection; @@ -77,6 +78,12 @@ namespace MediaBrowser.Common IReadOnlyList<IPlugin> Plugins { get; } /// <summary> + /// Gets all plugin assemblies which implement a custom rest api. + /// </summary> + /// <returns>An <see cref="IEnumerable{Assembly}"/> containing the plugin assemblies.</returns> + IEnumerable<Assembly> GetApiPluginAssemblies(); + + /// <summary> /// Notifies the pending restart. /// </summary> void NotifyPendingRestart(); @@ -116,8 +123,7 @@ namespace MediaBrowser.Common /// <summary> /// Initializes this instance. /// </summary> - /// <param name="serviceCollection">The service collection.</param> - void Init(IServiceCollection serviceCollection); + void Init(); /// <summary> /// Creates the instance. diff --git a/MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs b/MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs deleted file mode 100644 index fe5dd6cd4..000000000 --- a/MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Buffers; -using System.Buffers.Text; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Common.Json.Converters -{ - /// <summary> - /// Converts a GUID object or value to/from JSON. - /// </summary> - public class JsonInt32Converter : JsonConverter<int> - { - /// <inheritdoc /> - public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - static void ThrowFormatException() => throw new FormatException("Invalid format for an integer."); - ReadOnlySpan<byte> span = stackalloc byte[0]; - - if (reader.HasValueSequence) - { - long sequenceLength = reader.ValueSequence.Length; - Span<byte> stackSpan = stackalloc byte[(int)sequenceLength]; - reader.ValueSequence.CopyTo(stackSpan); - span = stackSpan; - } - else - { - span = reader.ValueSpan; - } - - if (!Utf8Parser.TryParse(span, out int number, out _)) - { - ThrowFormatException(); - } - - return number; - } - - /// <inheritdoc /> - public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options) - { - static void ThrowInvalidOperationException() => throw new InvalidOperationException(); - Span<byte> span = stackalloc byte[16]; - if (Utf8Formatter.TryFormat(value, span, out int bytesWritten)) - { - writer.WriteStringValue(span.Slice(0, bytesWritten)); - } - - ThrowInvalidOperationException(); - } - } -} diff --git a/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverter.cs b/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverter.cs deleted file mode 100644 index 0a36e1cb2..000000000 --- a/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverter.cs +++ /dev/null @@ -1,82 +0,0 @@ -#nullable enable - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Globalization; -using System.Reflection; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Common.Json.Converters -{ - /// <summary> - /// Converter for Dictionaries without string key. - /// TODO This can be removed when System.Text.Json supports Dictionaries with non-string keys. - /// </summary> - /// <typeparam name="TKey">Type of key.</typeparam> - /// <typeparam name="TValue">Type of value.</typeparam> - internal sealed class JsonNonStringKeyDictionaryConverter<TKey, TValue> : JsonConverter<IDictionary<TKey, TValue>> - { - /// <summary> - /// Read JSON. - /// </summary> - /// <param name="reader">The Utf8JsonReader.</param> - /// <param name="typeToConvert">The type to convert.</param> - /// <param name="options">The json serializer options.</param> - /// <returns>Typed dictionary.</returns> - /// <exception cref="NotSupportedException">Not supported.</exception> - public override IDictionary<TKey, TValue> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var convertedType = typeof(Dictionary<,>).MakeGenericType(typeof(string), typeToConvert.GenericTypeArguments[1]); - var value = JsonSerializer.Deserialize(ref reader, convertedType, options); - var instance = (Dictionary<TKey, TValue>)Activator.CreateInstance( - typeToConvert, - BindingFlags.Instance | BindingFlags.Public, - null, - null, - CultureInfo.CurrentCulture); - var enumerator = (IEnumerator)convertedType.GetMethod("GetEnumerator")!.Invoke(value, null); - var parse = typeof(TKey).GetMethod( - "Parse", - 0, - BindingFlags.Public | BindingFlags.Static, - null, - CallingConventions.Any, - new[] { typeof(string) }, - null); - if (parse == null) - { - throw new NotSupportedException($"{typeof(TKey)} as TKey in IDictionary<TKey, TValue> is not supported."); - } - - while (enumerator.MoveNext()) - { - var element = (KeyValuePair<string?, TValue>)enumerator.Current; - instance.Add((TKey)parse.Invoke(null, new[] { (object?)element.Key }), element.Value); - } - - return instance; - } - - /// <summary> - /// Write dictionary as Json. - /// </summary> - /// <param name="writer">The Utf8JsonWriter.</param> - /// <param name="value">The dictionary value.</param> - /// <param name="options">The Json serializer options.</param> - public override void Write(Utf8JsonWriter writer, IDictionary<TKey, TValue> value, JsonSerializerOptions options) - { - var convertedDictionary = new Dictionary<string?, TValue>(value.Count); - foreach (var (k, v) in value) - { - if (k != null) - { - convertedDictionary[k.ToString()] = v; - } - } - - JsonSerializer.Serialize(writer, convertedDictionary, options); - } - } -} diff --git a/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverterFactory.cs b/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverterFactory.cs deleted file mode 100644 index 52f360740..000000000 --- a/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverterFactory.cs +++ /dev/null @@ -1,59 +0,0 @@ -#nullable enable - -using System; -using System.Collections; -using System.Globalization; -using System.Reflection; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Common.Json.Converters -{ - /// <summary> - /// https://github.com/dotnet/runtime/issues/30524#issuecomment-524619972. - /// TODO This can be removed when System.Text.Json supports Dictionaries with non-string keys. - /// </summary> - internal sealed class JsonNonStringKeyDictionaryConverterFactory : JsonConverterFactory - { - /// <summary> - /// Only convert objects that implement IDictionary and do not have string keys. - /// </summary> - /// <param name="typeToConvert">Type convert.</param> - /// <returns>Conversion ability.</returns> - public override bool CanConvert(Type typeToConvert) - { - if (!typeToConvert.IsGenericType) - { - return false; - } - - // Let built in converter handle string keys - if (typeToConvert.GenericTypeArguments[0] == typeof(string)) - { - return false; - } - - // Only support objects that implement IDictionary - return typeToConvert.GetInterface(nameof(IDictionary)) != null; - } - - /// <summary> - /// Create converter for generic dictionary type. - /// </summary> - /// <param name="typeToConvert">Type to convert.</param> - /// <param name="options">Json serializer options.</param> - /// <returns>JsonConverter for given type.</returns> - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) - { - var converterType = typeof(JsonNonStringKeyDictionaryConverter<,>) - .MakeGenericType(typeToConvert.GenericTypeArguments[0], typeToConvert.GenericTypeArguments[1]); - var converter = (JsonConverter)Activator.CreateInstance( - converterType, - BindingFlags.Instance | BindingFlags.Public, - null, - null, - CultureInfo.CurrentCulture); - return converter; - } - } -} diff --git a/MediaBrowser.Common/Json/Converters/JsonNullableStructConverter.cs b/MediaBrowser.Common/Json/Converters/JsonNullableStructConverter.cs new file mode 100644 index 000000000..cffc41ba3 --- /dev/null +++ b/MediaBrowser.Common/Json/Converters/JsonNullableStructConverter.cs @@ -0,0 +1,44 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace MediaBrowser.Common.Json.Converters +{ + /// <summary> + /// Converts a nullable struct or value to/from JSON. + /// Required - some clients send an empty string. + /// </summary> + /// <typeparam name="T">The struct type.</typeparam> + public class JsonNullableStructConverter<T> : JsonConverter<T?> + where T : struct + { + private readonly JsonConverter<T?> _baseJsonConverter; + + /// <summary> + /// Initializes a new instance of the <see cref="JsonNullableStructConverter{T}"/> class. + /// </summary> + /// <param name="baseJsonConverter">The base json converter.</param> + public JsonNullableStructConverter(JsonConverter<T?> baseJsonConverter) + { + _baseJsonConverter = baseJsonConverter; + } + + /// <inheritdoc /> + public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + // Handle empty string. + if (reader.TokenType == JsonTokenType.String && ((reader.HasValueSequence && reader.ValueSequence.IsEmpty) || reader.ValueSpan.IsEmpty)) + { + return null; + } + + return _baseJsonConverter.Read(ref reader, typeToConvert, options); + } + + /// <inheritdoc /> + public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options) + { + _baseJsonConverter.Write(writer, value, options); + } + } +} diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs index 78a458add..5867cd4a0 100644 --- a/MediaBrowser.Common/Json/JsonDefaults.cs +++ b/MediaBrowser.Common/Json/JsonDefaults.cs @@ -12,19 +12,54 @@ namespace MediaBrowser.Common.Json /// <summary> /// Gets the default <see cref="JsonSerializerOptions" /> options. /// </summary> + /// <remarks> + /// When changing these options, update + /// Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs + /// -> AddJellyfinApi + /// -> AddJsonOptions. + /// </remarks> /// <returns>The default <see cref="JsonSerializerOptions" /> options.</returns> public static JsonSerializerOptions GetOptions() { - var options = new JsonSerializerOptions() + var options = new JsonSerializerOptions { ReadCommentHandling = JsonCommentHandling.Disallow, - WriteIndented = false + WriteIndented = false, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + NumberHandling = JsonNumberHandling.AllowReadingFromString }; + // Get built-in converters for fallback converting. + var baseNullableInt32Converter = (JsonConverter<int?>)options.GetConverter(typeof(int?)); + var baseNullableInt64Converter = (JsonConverter<long?>)options.GetConverter(typeof(long?)); + options.Converters.Add(new JsonGuidConverter()); options.Converters.Add(new JsonStringEnumConverter()); - options.Converters.Add(new JsonNonStringKeyDictionaryConverterFactory()); + options.Converters.Add(new JsonNullableStructConverter<int>(baseNullableInt32Converter)); + options.Converters.Add(new JsonNullableStructConverter<long>(baseNullableInt64Converter)); + + return options; + } + /// <summary> + /// Gets camelCase json options. + /// </summary> + /// <returns>The camelCase <see cref="JsonSerializerOptions" /> options.</returns> + public static JsonSerializerOptions GetCamelCaseOptions() + { + var options = GetOptions(); + options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; + return options; + } + + /// <summary> + /// Gets PascalCase json options. + /// </summary> + /// <returns>The PascalCase <see cref="JsonSerializerOptions" /> options.</returns> + public static JsonSerializerOptions GetPascalCaseOptions() + { + var options = GetOptions(); + options.PropertyNamingPolicy = null; return options; } } diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 7380f39fd..70dcc2397 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -8,8 +8,9 @@ <PropertyGroup> <Authors>Jellyfin Contributors</Authors> <PackageId>Jellyfin.Common</PackageId> - <PackageLicenseUrl>https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt</PackageLicenseUrl> + <VersionPrefix>10.7.0</VersionPrefix> <RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl> + <PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression> </PropertyGroup> <ItemGroup> @@ -17,8 +18,9 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.6" /> - <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.6" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.7" /> + <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.7" /> + <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/> <PackageReference Include="Microsoft.Net.Http.Headers" Version="2.2.8" /> </ItemGroup> @@ -31,6 +33,15 @@ <GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateDocumentationFile>true</GenerateDocumentationFile> <TreatWarningsAsErrors>true</TreatWarningsAsErrors> + <PublishRepositoryUrl>true</PublishRepositoryUrl> + <EmbedUntrackedSources>true</EmbedUntrackedSources> + <IncludeSymbols>true</IncludeSymbols> + <SymbolPackageFormat>snupkg</SymbolPackageFormat> + </PropertyGroup> + + <PropertyGroup Condition=" '$(Stability)'=='Unstable'"> + <!-- Include all symbols in the main nupkg until Azure Artifact Feed starts supporting ingesting NuGet symbol packages. --> + <AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder> </PropertyGroup> <!-- Code analyzers--> diff --git a/MediaBrowser.Common/Net/DefaultHttpClientHandler.cs b/MediaBrowser.Common/Net/DefaultHttpClientHandler.cs new file mode 100644 index 000000000..e189d6e70 --- /dev/null +++ b/MediaBrowser.Common/Net/DefaultHttpClientHandler.cs @@ -0,0 +1,20 @@ +using System.Net; +using System.Net.Http; + +namespace MediaBrowser.Common.Net +{ + /// <summary> + /// Default http client handler. + /// </summary> + public class DefaultHttpClientHandler : HttpClientHandler + { + /// <summary> + /// Initializes a new instance of the <see cref="DefaultHttpClientHandler"/> class. + /// </summary> + public DefaultHttpClientHandler() + { + // TODO change to DecompressionMethods.All with .NET5 + AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + } + } +} diff --git a/MediaBrowser.Common/Net/NamedClient.cs b/MediaBrowser.Common/Net/NamedClient.cs new file mode 100644 index 000000000..0f6161c32 --- /dev/null +++ b/MediaBrowser.Common/Net/NamedClient.cs @@ -0,0 +1,18 @@ +namespace MediaBrowser.Common.Net +{ + /// <summary> + /// Registered http client names. + /// </summary> + public static class NamedClient + { + /// <summary> + /// Gets the value for the default named http client. + /// </summary> + public const string Default = nameof(Default); + + /// <summary> + /// Gets the value for the MusicBrainz named http client. + /// </summary> + public const string MusicBrainz = nameof(MusicBrainz); + } +} diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs index f10a1918f..4b2918d08 100644 --- a/MediaBrowser.Common/Plugins/BasePlugin.cs +++ b/MediaBrowser.Common/Plugins/BasePlugin.cs @@ -6,6 +6,7 @@ using System.Reflection; using MediaBrowser.Common.Configuration; using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Serialization; +using Microsoft.Extensions.DependencyInjection; namespace MediaBrowser.Common.Plugins { @@ -82,6 +83,16 @@ namespace MediaBrowser.Common.Plugins } /// <inheritdoc /> + public virtual void RegisterServices(IServiceCollection serviceCollection) + { + } + + /// <inheritdoc /> + public virtual void UnregisterServices(IServiceCollection serviceCollection) + { + } + + /// <inheritdoc /> public void SetAttributes(string assemblyFilePath, string dataFolderPath, Version assemblyVersion) { AssemblyFilePath = assemblyFilePath; diff --git a/MediaBrowser.Common/Plugins/IPlugin.cs b/MediaBrowser.Common/Plugins/IPlugin.cs index 7bd37d210..1844eb124 100644 --- a/MediaBrowser.Common/Plugins/IPlugin.cs +++ b/MediaBrowser.Common/Plugins/IPlugin.cs @@ -2,6 +2,7 @@ using System; using MediaBrowser.Model.Plugins; +using Microsoft.Extensions.DependencyInjection; namespace MediaBrowser.Common.Plugins { @@ -61,6 +62,18 @@ namespace MediaBrowser.Common.Plugins /// Called when just before the plugin is uninstalled from the server. /// </summary> void OnUninstalling(); + + /// <summary> + /// Registers the plugin's services to the service collection. + /// </summary> + /// <param name="serviceCollection">The service collection.</param> + void RegisterServices(IServiceCollection serviceCollection); + + /// <summary> + /// Unregisters the plugin's services from the service collection. + /// </summary> + /// <param name="serviceCollection">The service collection.</param> + void UnregisterServices(IServiceCollection serviceCollection); } public interface IHasPluginConfiguration diff --git a/MediaBrowser.Common/Updates/InstallationEventArgs.cs b/MediaBrowser.Common/Updates/InstallationEventArgs.cs index 11eb2ad34..61178f631 100644 --- a/MediaBrowser.Common/Updates/InstallationEventArgs.cs +++ b/MediaBrowser.Common/Updates/InstallationEventArgs.cs @@ -1,10 +1,11 @@ #pragma warning disable CS1591 +using System; using MediaBrowser.Model.Updates; namespace MediaBrowser.Common.Updates { - public class InstallationEventArgs + public class InstallationEventArgs : EventArgs { public InstallationInfo InstallationInfo { get; set; } |
