// Copyright (c) All contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Collections.Generic; using System.Text.RegularExpressions; using Nerdbank.Streams; namespace MessagePack { #if !DYNAMICCODEDUMPER /// /// An immutable description of options for running the . /// #endif public class MessagePackSerializerOptions { // see:http://msdn.microsoft.com/en-us/library/w3f99sx1.aspx internal static readonly Regex AssemblyNameVersionSelectorRegex = new Regex(@", Version=\d+.\d+.\d+.\d+, Culture=[\w-]+, PublicKeyToken=(?:null|[a-f0-9]{16})", RegexOptions.Compiled); /// /// A collection of known dangerous types that are not expected in a typical MessagePack stream, /// and thus are rejected by the default implementation of . /// private static readonly HashSet DisallowedTypes = new HashSet { "System.CodeDom.Compiler.TempFileCollection", "System.Management.IWbemClassObjectFreeThreaded", }; #if !DYNAMICCODEDUMPER /// /// Gets a good default set of options that uses the and no compression. /// public static MessagePackSerializerOptions Standard => MessagePackSerializerOptionsDefaultSettingsLazyInitializationHelper.Standard; #endif /// /// Initializes a new instance of the class. /// protected internal MessagePackSerializerOptions(IFormatterResolver resolver) { this.Resolver = resolver ?? throw new ArgumentNullException(nameof(resolver)); } /// /// Initializes a new instance of the class /// with members initialized from an existing instance. /// /// The options to copy from. protected MessagePackSerializerOptions(MessagePackSerializerOptions copyFrom) { if (copyFrom == null) { throw new ArgumentNullException(nameof(copyFrom)); } this.Resolver = copyFrom.Resolver; this.Compression = copyFrom.Compression; this.OldSpec = copyFrom.OldSpec; this.OmitAssemblyVersion = copyFrom.OmitAssemblyVersion; this.AllowAssemblyVersionMismatch = copyFrom.AllowAssemblyVersionMismatch; this.Security = copyFrom.Security; this.SequencePool = copyFrom.SequencePool; } /// /// Gets the resolver to use for complex types. /// /// An instance of . Never null. /// Thrown if an attempt is made to set this property to null. public IFormatterResolver Resolver { get; private set; } /// /// Gets the compression scheme to apply to serialized sequences. /// /// /// When set to something other than , /// deserialization can still work on uncompressed sequences, /// and serialization may not compress if msgpack sequences are short enough that compression would not likely be advantageous. /// public MessagePackCompression Compression { get; private set; } /// /// Gets a value indicating whether to serialize with set to some value /// causing messagepack spec compliance to be explicitly set to the old or new format. /// /// /// A null value means the 's default or previous setting will be used. /// A non-null value will be applied to the property for the duration of a /// serialization and then reverted to its prior setting. /// /// /// Reading always supports both new and old spec. /// public bool? OldSpec { get; private set; } /// /// Gets a value indicating whether serialization should omit assembly version, culture and public key token metadata when using the typeless formatter. /// /// The default value is false. public bool OmitAssemblyVersion { get; private set; } /// /// Gets a value indicating whether deserialization may instantiate types from an assembly with a different version if a matching version cannot be found. /// /// The default value is false. public bool AllowAssemblyVersionMismatch { get; private set; } /// /// Gets the security-related options for deserializing messagepack sequences. /// /// /// The default value is to use . /// public MessagePackSecurity Security { get; private set; } = MessagePackSecurity.TrustedData; /// /// Gets a thread-safe pool of reusable objects. /// /// The default value is the instance. public SequencePool SequencePool { get; private set; } = SequencePool.Shared; /// /// Gets a type given a string representation of the type. /// /// The name of the type to load. This is typically the but may use the assembly's simple name. /// The loaded type or null if no matching type could be found. public virtual Type LoadType(string typeName) { Type result = Type.GetType(typeName, false); if (result == null && this.AllowAssemblyVersionMismatch) { string shortenedName = AssemblyNameVersionSelectorRegex.Replace(typeName, string.Empty); if (shortenedName != typeName) { result = Type.GetType(shortenedName, false); } } return result; } /// /// Checks whether a given type may be deserialized. /// /// The type to be instantiated. /// Thrown if the is not allowed to be deserialized. /// /// This method provides a means for an important security mitigation when using the Typeless formatter to prevent untrusted messagepack from /// deserializing objects that may be harmful if instantiated, disposed or finalized. /// The default implementation throws for only a few known dangerous types. /// Applications that deserialize from untrusted sources should override this method and throw if the type is not among the expected set. /// public virtual void ThrowIfDeserializingTypeIsDisallowed(Type type) { if (DisallowedTypes.Contains(type.FullName)) { throw new MessagePackSerializationException("Deserialization attempted to create the type " + type.FullName + " which is not allowed."); } } /// /// Gets a copy of these options with the property set to a new value. /// /// The new value for the . /// The new instance; or the original if the value is unchanged. public MessagePackSerializerOptions WithResolver(IFormatterResolver resolver) { if (this.Resolver == resolver) { return this; } var result = this.Clone(); result.Resolver = resolver; return result; } /// /// Gets a copy of these options with the property set to a new value. /// /// The new value for the property. /// The new instance; or the original if the value is unchanged. public MessagePackSerializerOptions WithCompression(MessagePackCompression compression) { if (this.Compression == compression) { return this; } var result = this.Clone(); result.Compression = compression; return result; } /// /// Gets a copy of these options with the property set to a new value. /// /// The new value for the . /// The new instance; or the original if the value is unchanged. public MessagePackSerializerOptions WithOldSpec(bool? oldSpec = true) { if (this.OldSpec == oldSpec) { return this; } var result = this.Clone(); result.OldSpec = oldSpec; return result; } /// /// Gets a copy of these options with the property set to a new value. /// /// The new value for the property. /// The new instance; or the original if the value is unchanged. public MessagePackSerializerOptions WithOmitAssemblyVersion(bool omitAssemblyVersion) { if (this.OmitAssemblyVersion == omitAssemblyVersion) { return this; } var result = this.Clone(); result.OmitAssemblyVersion = omitAssemblyVersion; return result; } /// /// Gets a copy of these options with the property set to a new value. /// /// The new value for the property. /// The new instance; or the original if the value is unchanged. public MessagePackSerializerOptions WithAllowAssemblyVersionMismatch(bool allowAssemblyVersionMismatch) { if (this.AllowAssemblyVersionMismatch == allowAssemblyVersionMismatch) { return this; } var result = this.Clone(); result.AllowAssemblyVersionMismatch = allowAssemblyVersionMismatch; return result; } /// /// Gets a copy of these options with the property set to a new value. /// /// The new value for the property. /// The new instance; or the original if the value is unchanged. public MessagePackSerializerOptions WithSecurity(MessagePackSecurity security) { if (security is null) { throw new ArgumentNullException(nameof(security)); } if (this.Security == security) { return this; } var result = this.Clone(); result.Security = security; return result; } /// /// Gets a copy of these options with the property set to a new value. /// /// The new value for the property. /// The new instance. public MessagePackSerializerOptions WithPool(SequencePool pool) { if (pool is null) { throw new ArgumentNullException(nameof(pool)); } if (this.SequencePool == pool) { return this; } var result = this.Clone(); result.SequencePool = pool; return result; } /// /// Creates a clone of this instance with the same properties set. /// /// The cloned instance. Guaranteed to be a new instance. /// Thrown if this instance is a derived type that doesn't override this method. protected virtual MessagePackSerializerOptions Clone() { if (this.GetType() != typeof(MessagePackSerializerOptions)) { throw new NotSupportedException($"The derived type {this.GetType().FullName} did not override the {nameof(Clone)} method as required."); } return new MessagePackSerializerOptions(this); } #if !DYNAMICCODEDUMPER private static class MessagePackSerializerOptionsDefaultSettingsLazyInitializationHelper { public static readonly MessagePackSerializerOptions Standard = new MessagePackSerializerOptions(Resolvers.StandardResolver.Instance); } #endif } }