// 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
}
}