diff options
214 files changed, 6818 insertions, 3258 deletions
diff --git a/BuildToolsVersion.txt b/BuildToolsVersion.txt index 7d7e2d494c..ae89f1826c 100644 --- a/BuildToolsVersion.txt +++ b/BuildToolsVersion.txt @@ -1 +1 @@ -2.1.0-preview3-02629-02 +2.1.0-preview2-02629-01 diff --git a/Documentation/project-docs/issue-guide.md b/Documentation/project-docs/issue-guide.md index 5e0fc52ea3..008222056b 100644 --- a/Documentation/project-docs/issue-guide.md +++ b/Documentation/project-docs/issue-guide.md @@ -55,8 +55,8 @@ Areas are tracked by labels area-* (e.g. area-System.Collections). Each area | [System.Composition](https://github.com/dotnet/corefx/labels/area-System.Composition) | **[@maryamariyan](https://github.com/maryamariyan)**, [@ViktorHofer](https://github.com/ViktorHofer) | | | [System.Configuration](https://github.com/dotnet/corefx/labels/area-System.Configuration) | [@maryamariyan](https://github.com/maryamariyan) | | | [System.Console](https://github.com/dotnet/corefx/labels/area-System.Console) | **[@joperezr](https://github.com/joperezr)**, [@ianhays](https://github.com/ianhays) | | -| [System.Data](https://github.com/dotnet/corefx/labels/area-System.Data) | **[@divega](https://github.com/divega)**, [@ajcvickers](https://github.com/ajcvickers), [@keeratsingh](https://github.com/keeratsingh), [@afsanehr](https://github.com/afsanehr), [@david-engel](https://github.com/david-engel), [@saurabh500](https://github.com/saurabh500) | | -| [System.Data.SqlClient](https://github.com/dotnet/corefx/labels/area-System.Data.SqlClient) | **[@keeratsingh](https://github.com/keeratsingh)**, [@afsanehr](https://github.com/afsanehr), [@david-engel](https://github.com/david-engel), [@saurabh500](https://github.com/saurabh500) | | +| [System.Data](https://github.com/dotnet/corefx/labels/area-System.Data) | **[@divega](https://github.com/divega)**, [@ajcvickers](https://github.com/ajcvickers), [@keeratsingh](https://github.com/keeratsingh), [@afsanehr](https://github.com/afsanehr), [@david-engel](https://github.com/david-engel) | | +| [System.Data.SqlClient](https://github.com/dotnet/corefx/labels/area-System.Data.SqlClient) | **[@keeratsingh](https://github.com/keeratsingh)**, [@afsanehr](https://github.com/afsanehr), [@david-engel](https://github.com/david-engel) | | | [System.Diagnostics](https://github.com/dotnet/corefx/labels/area-System.Diagnostics) | **[@joperezr](https://github.com/joperezr)**, [@wtgodbe](https://github.com/wtgodbe) | <ul><li>System.Diagnostics.EventLog [@Anipik](https://github.com/Anipik)</li></ul> | | [System.Diagnostics.Process](https://github.com/dotnet/corefx/labels/area-System.Diagnostics.Process) | **[@joperezr](https://github.com/joperezr)**, [@wtgodbe](https://github.com/wtgodbe) | | | [System.Diagnostics.Tracing](https://github.com/dotnet/corefx/labels/area-System.Diagnostics.Tracing) | [@brianrob](https://github.com/brianrob), [@vancem](https://github.com/vancem), [@valenis](https://github.com/valenis) | Packages:<ul><li>System.Diagnostics.DiagnosticSource</li><li>System.Diagnostics.PerformanceCounter - [@adiaaida](https://github.com/adiaaida)</li><li>System.Diagnostics.Tracing</li><li>System.Diagnostics.TraceSource</li></ul><br/> | diff --git a/dependencies.props b/dependencies.props index a3f91b9be6..fd03675b90 100644 --- a/dependencies.props +++ b/dependencies.props @@ -9,15 +9,15 @@ These ref versions are pulled from https://github.com/dotnet/versions. --> <PropertyGroup> - <CoreFxCurrentRef>557c1e3cd387554127d14604466e78d45af8df54</CoreFxCurrentRef> - <CoreClrCurrentRef>68931eec8b1b9daed99ab9fbe0baf26fd0637dc0</CoreClrCurrentRef> - <CoreSetupCurrentRef>557c1e3cd387554127d14604466e78d45af8df54</CoreSetupCurrentRef> + <CoreFxCurrentRef>d5ab69777647515ec8671bd46ad19aeafa289dd1</CoreFxCurrentRef> + <CoreClrCurrentRef>d5ab69777647515ec8671bd46ad19aeafa289dd1</CoreClrCurrentRef> + <CoreSetupCurrentRef>d5ab69777647515ec8671bd46ad19aeafa289dd1</CoreSetupCurrentRef> <ExternalCurrentRef>96dc7805f5df4a70a55783964ce69dcd91bfca80</ExternalCurrentRef> - <ProjectNTfsCurrentRef>80227ec4e1f84877b5298d87680848011ad6462b</ProjectNTfsCurrentRef> - <ProjectNTfsTestILCCurrentRef>80227ec4e1f84877b5298d87680848011ad6462b</ProjectNTfsTestILCCurrentRef> + <ProjectNTfsCurrentRef>5c5d8906d00f58ed9c9e00595212ac4cdb0028b3</ProjectNTfsCurrentRef> + <ProjectNTfsTestILCCurrentRef>5c5d8906d00f58ed9c9e00595212ac4cdb0028b3</ProjectNTfsTestILCCurrentRef> <SniCurrentRef>8bd1ec5fac9f0eec34ff6b34b1d878b4359e02dd</SniCurrentRef> <StandardCurrentRef>6298244e25cf84d91e3cda9627315f2425274624</StandardCurrentRef> - <BuildToolsCurrentRef>68931eec8b1b9daed99ab9fbe0baf26fd0637dc0</BuildToolsCurrentRef> + <BuildToolsCurrentRef>d5ab69777647515ec8671bd46ad19aeafa289dd1</BuildToolsCurrentRef> </PropertyGroup> <!-- Product dependency versions. --> @@ -31,16 +31,15 @@ <!-- Tests/infrastructure dependency versions. --> <PropertyGroup> - <CoreFxExpectedPrerelease>preview3-26329-05</CoreFxExpectedPrerelease> - <MicrosoftNETCorePlatformsPackageVersion>2.1.0-preview3-26329-05</MicrosoftNETCorePlatformsPackageVersion> - <MicrosoftNETCoreRuntimeCoreCLRPackageVersion>2.1.0-preview3-26329-01</MicrosoftNETCoreRuntimeCoreCLRPackageVersion> - <ProjectNTfsExpectedPrerelease>beta-26329-00</ProjectNTfsExpectedPrerelease> - <ProjectNTfsTestILCExpectedPrerelease>beta-26329-00</ProjectNTfsTestILCExpectedPrerelease> - <ProjectNTfsTestILCPackageVersion>1.0.0-beta-26329-00</ProjectNTfsTestILCPackageVersion> - <MicrosoftNETCoreDotNetHostPackageVersion>2.1.0-preview3-26329-02</MicrosoftNETCoreDotNetHostPackageVersion> - <MicrosoftNETCoreDotNetHostPolicyPackageVersion>2.1.0-preview3-26329-02</MicrosoftNETCoreDotNetHostPolicyPackageVersion> - <MicrosoftNETCoreAppPackageVersion>2.1.0-preview3-26329-02</MicrosoftNETCoreAppPackageVersion> - <MicrosoftDotNetPlatformAbstractionsPackageVersion>2.1.0-preview3-26329-02</MicrosoftDotNetPlatformAbstractionsPackageVersion> + <CoreFxExpectedPrerelease>preview2-26403-05</CoreFxExpectedPrerelease> + <MicrosoftNETCorePlatformsPackageVersion>2.1.0-preview2-26403-05</MicrosoftNETCorePlatformsPackageVersion> + <MicrosoftNETCoreRuntimeCoreCLRPackageVersion>2.1.0-preview2-26403-07</MicrosoftNETCoreRuntimeCoreCLRPackageVersion> + <ProjectNTfsExpectedPrerelease>beta-26330-00</ProjectNTfsExpectedPrerelease> + <ProjectNTfsTestILCExpectedPrerelease>beta-26330-00</ProjectNTfsTestILCExpectedPrerelease> + <ProjectNTfsTestILCPackageVersion>1.0.0-beta-26330-00</ProjectNTfsTestILCPackageVersion> + <MicrosoftNETCoreDotNetHostPackageVersion>2.1.0-preview2-26403-06</MicrosoftNETCoreDotNetHostPackageVersion> + <MicrosoftNETCoreDotNetHostPolicyPackageVersion>2.1.0-preview2-26403-06</MicrosoftNETCoreDotNetHostPolicyPackageVersion> + <MicrosoftNETCoreAppPackageVersion>2.1.0-preview2-26403-06</MicrosoftNETCoreAppPackageVersion> <!-- CoreFX-built SNI identity package --> <RuntimeNativeSystemDataSqlClientSniPackageVersion>4.4.0</RuntimeNativeSystemDataSqlClientSniPackageVersion> @@ -48,7 +47,7 @@ <AppXRunnerVersion>1.0.3-prerelease-00921-01</AppXRunnerVersion> <XunitPerfAnalysisPackageVersion>1.0.0-beta-build0018</XunitPerfAnalysisPackageVersion> <TraceEventPackageVersion>2.0.5</TraceEventPackageVersion> - <XunitNetcoreExtensionsVersion>2.1.0-preview3-02629-02</XunitNetcoreExtensionsVersion> + <XunitNetcoreExtensionsVersion>2.1.0-preview2-02629-01</XunitNetcoreExtensionsVersion> <!-- Roslyn optimization data package version --> <OptimizationDataVersion>2.0.0-rc-61101-17</OptimizationDataVersion> @@ -57,7 +56,7 @@ <!-- Package versions used as toolsets --> <PropertyGroup> <FeedTasksPackage>Microsoft.DotNet.Build.Tasks.Feed</FeedTasksPackage> - <FeedTasksPackageVersion>2.1.0-preview3-02629-02</FeedTasksPackageVersion> + <FeedTasksPackageVersion>2.1.0-preview2-02629-01</FeedTasksPackageVersion> </PropertyGroup> <!-- Publish symbol build task package --> @@ -176,11 +175,6 @@ <ElementName>MicrosoftNETCoreAppPackageVersion</ElementName> <PackageId>Microsoft.NETCore.App</PackageId> </XmlUpdateStep> - <XmlUpdateStep Include="CoreSetup"> - <Path>$(MSBuildThisFileFullPath)</Path> - <ElementName>MicrosoftDotNetPlatformAbstractionsPackageVersion</ElementName> - <PackageId>Microsoft.DotNet.PlatformAbstractions</PackageId> - </XmlUpdateStep> <UpdateStep Include="BuildTools"> <UpdaterType>File</UpdaterType> <Path>$(MSBuildThisFileDirectory)BuildToolsVersion.txt</Path> diff --git a/external/test-runtime/XUnit.Runtime.depproj b/external/test-runtime/XUnit.Runtime.depproj index 8e8f63db98..2b96e94ba3 100644 --- a/external/test-runtime/XUnit.Runtime.depproj +++ b/external/test-runtime/XUnit.Runtime.depproj @@ -51,7 +51,7 @@ <Version>0.10.0-alpha-experimental</Version> </PackageReference> <PackageReference Condition="'$(TargetGroup)' != 'uapaot'" Include="Microsoft.DotNet.PlatformAbstractions"> - <Version>$(MicrosoftDotNetPlatformAbstractionsPackageVersion)</Version> + <Version>2.0.4</Version> </PackageReference> <PackageReference Include="CommandLineParser"> <Version>2.1.1-beta</Version> diff --git a/external/test-runtime/optional.json b/external/test-runtime/optional.json index b399048807..777514af15 100644 --- a/external/test-runtime/optional.json +++ b/external/test-runtime/optional.json @@ -3,9 +3,9 @@ "net45": { "dependencies": { "Microsoft.DotNet.IBCMerge": "4.6.0-alpha-00001", - "TestILC.amd64ret": "1.0.0-beta-26329-00", - "TestILC.armret": "1.0.0-beta-26329-00", - "TestILC.x86ret": "1.0.0-beta-26329-00" + "TestILC.amd64ret": "1.0.0-beta-26330-00", + "TestILC.armret": "1.0.0-beta-26330-00", + "TestILC.x86ret": "1.0.0-beta-26330-00" } } } diff --git a/pkg/Microsoft.Private.PackageBaseline/packageIndex.json b/pkg/Microsoft.Private.PackageBaseline/packageIndex.json index ea787d868e..c80d67c06a 100644 --- a/pkg/Microsoft.Private.PackageBaseline/packageIndex.json +++ b/pkg/Microsoft.Private.PackageBaseline/packageIndex.json @@ -1238,7 +1238,7 @@ "monotouch10": "Any", "net461": "4.1.0.0", "uap10.0.16299": "4.3.0.0", - "uap10.0.16300": "4.3.1.0", + "uap10.0.16300": "4.4.0.0", "xamarinios10": "Any", "xamarinmac20": "Any", "xamarintvos10": "Any", @@ -1249,7 +1249,7 @@ "4.1.0.0": "4.1.0", "4.1.1.0": "4.3.0", "4.2.0.0": "4.4.0", - "4.3.1.0": "4.5.0" + "4.4.0.0": "4.5.0" } }, "System.Data.SqlXml": { diff --git a/pkg/test/packageSettings/System.IO.Packaging/netcoreapp1.0/workaroundDowngrade.targets b/pkg/test/packageSettings/System.IO.Packaging/netcoreapp1.0/workaroundDowngrade.targets new file mode 100644 index 0000000000..1ec61540e4 --- /dev/null +++ b/pkg/test/packageSettings/System.IO.Packaging/netcoreapp1.0/workaroundDowngrade.targets @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <!-- issue https://github.com/dotnet/corefx/issues/28551 --> + <PackageReference Include="NETStandard.Library" Version="1.6.1" /> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/pkg/test/packageSettings/System.Security.Cryptography.Pkcs/netcoreapp1.0/workaroundDowngrade.targets b/pkg/test/packageSettings/System.Security.Cryptography.Pkcs/netcoreapp1.0/workaroundDowngrade.targets new file mode 100644 index 0000000000..1ec61540e4 --- /dev/null +++ b/pkg/test/packageSettings/System.Security.Cryptography.Pkcs/netcoreapp1.0/workaroundDowngrade.targets @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <!-- issue https://github.com/dotnet/corefx/issues/28551 --> + <PackageReference Include="NETStandard.Library" Version="1.6.1" /> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/src/Common/src/CoreLib/System.Private.CoreLib.Shared.projitems b/src/Common/src/CoreLib/System.Private.CoreLib.Shared.projitems index 1143580173..d5c432e128 100644 --- a/src/Common/src/CoreLib/System.Private.CoreLib.Shared.projitems +++ b/src/Common/src/CoreLib/System.Private.CoreLib.Shared.projitems @@ -46,9 +46,10 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\ArrayPool.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\ArrayPoolEventSource.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\ConfigurableArrayPool.cs" /> - <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\IRetainable.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\IMemoryOwner.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\IPinnable.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\MemoryHandle.cs" /> - <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\OwnedMemory.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\MemoryManager.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\TlsOverPerCoreLockedStacksArrayPool.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Utilities.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Byte.cs" /> diff --git a/src/Common/src/CoreLib/System/Buffers/IMemoryOwner.cs b/src/Common/src/CoreLib/System/Buffers/IMemoryOwner.cs new file mode 100644 index 0000000000..a796fbbadf --- /dev/null +++ b/src/Common/src/CoreLib/System/Buffers/IMemoryOwner.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Buffers +{ + /// <summary> + /// Owner of Memory<typeparamref name="T"/> that is responsible for disposing the underlying memory appropriately. + /// </summary> + public interface IMemoryOwner<T> : IDisposable + { + /// <summary> + /// Returns a Memory<typeparamref name="T"/>. + /// </summary> + Memory<T> Memory { get; } + + } +} diff --git a/src/Common/src/CoreLib/System/Buffers/IPinnable.cs b/src/Common/src/CoreLib/System/Buffers/IPinnable.cs new file mode 100644 index 0000000000..623e716a05 --- /dev/null +++ b/src/Common/src/CoreLib/System/Buffers/IPinnable.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Buffers +{ + /// <summary> + /// Provides a mechanism for pinning and unpinning objects to prevent the GC from moving them. + /// </summary> + public interface IPinnable + { + /// <summary> + /// Call this method to indicate that the IPinnable object can not be moved by the garbage collector. + /// The address of the pinned object can be taken. + /// <param name="elementIndex">The offset to the element within the memory at which the returned <see cref="MemoryHandle"/> points to.</param> + /// </summary> + MemoryHandle Pin(int elementIndex); + + /// <summary> + /// Call this method to indicate that the IPinnable object no longer needs to be pinned. + /// The garbage collector is free to move the object now. + /// </summary> + void Unpin(); + } +} diff --git a/src/Common/src/CoreLib/System/Buffers/IRetainable.cs b/src/Common/src/CoreLib/System/Buffers/IRetainable.cs deleted file mode 100644 index 6ac508859c..0000000000 --- a/src/Common/src/CoreLib/System/Buffers/IRetainable.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Runtime; -using System.Runtime.CompilerServices; - -namespace System.Buffers -{ - /// <summary> - /// Provides a mechanism for manual lifetime management. - /// </summary> - public interface IRetainable - { - /// <summary> - /// Call this method to indicate that the IRetainable object is in use. - /// Do not dispose until Release is called. - /// </summary> - void Retain(); - /// <summary> - /// Call this method to indicate that the IRetainable object is no longer in use. - /// The object can now be disposed. - /// </summary> - bool Release(); - } -}
\ No newline at end of file diff --git a/src/Common/src/CoreLib/System/Buffers/MemoryHandle.cs b/src/Common/src/CoreLib/System/Buffers/MemoryHandle.cs index 7544038629..8859b49953 100644 --- a/src/Common/src/CoreLib/System/Buffers/MemoryHandle.cs +++ b/src/Common/src/CoreLib/System/Buffers/MemoryHandle.cs @@ -12,37 +12,32 @@ namespace System.Buffers /// </summary> public unsafe struct MemoryHandle : IDisposable { - private IRetainable _retainable; private void* _pointer; private GCHandle _handle; + private IPinnable _pinnable; /// <summary> /// Creates a new memory handle for the memory. /// </summary> - /// <param name="retainable">reference to manually managed object</param> - /// <param name="pointer">pointer to memory, or null if a pointer was not provided when the handle was created</param> + /// <param name="pointer">pointer to memory</param> + /// <param name="pinnable">reference to manually managed object, or default if there is no memory manager</param> /// <param name="handle">handle used to pin array buffers</param> [CLSCompliant(false)] - public MemoryHandle(IRetainable retainable, void* pointer = null, GCHandle handle = default(GCHandle)) + public MemoryHandle(void* pointer, GCHandle handle = default, IPinnable pinnable = default) { - _retainable = retainable; _pointer = pointer; _handle = handle; + _pinnable = pinnable; } /// <summary> - /// Returns the pointer to memory, or null if a pointer was not provided when the handle was created. + /// Returns the pointer to memory, where the memory is assumed to be pinned and hence the address won't change. /// </summary> [CLSCompliant(false)] public void* Pointer => _pointer; /// <summary> - /// Returns false if the pointer to memory is null. - /// </summary> - public bool HasPointer => _pointer != null; - - /// <summary> - /// Frees the pinned handle and releases IRetainable. + /// Frees the pinned handle and releases IPinnable. /// </summary> public void Dispose() { @@ -51,14 +46,14 @@ namespace System.Buffers _handle.Free(); } - if (_retainable != null) + if (_pinnable != null) { - _retainable.Release(); - _retainable = null; + _pinnable.Unpin(); + _pinnable = null; } _pointer = null; } } -}
\ No newline at end of file +} diff --git a/src/Common/src/CoreLib/System/Buffers/MemoryManager.cs b/src/Common/src/CoreLib/System/Buffers/MemoryManager.cs new file mode 100644 index 0000000000..cc47e02a27 --- /dev/null +++ b/src/Common/src/CoreLib/System/Buffers/MemoryManager.cs @@ -0,0 +1,66 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime; +using System.Runtime.CompilerServices; + +namespace System.Buffers +{ + /// <summary> + /// Manager of Memory<typeparamref name="T"/> that provides the implementation. + /// </summary> + public abstract class MemoryManager<T> : IMemoryOwner<T>, IPinnable + { + /// <summary> + /// The number of items in the Memory<typeparamref name="T"/>. + /// </summary> + public virtual int Length => GetSpan().Length; + + /// <summary> + /// Returns a Memory<typeparamref name="T"/>. + /// </summary> + public virtual Memory<T> Memory => new Memory<T>(this, 0, Length); + + /// <summary> + /// Returns a span wrapping the underlying memory. + /// </summary> + public abstract Span<T> GetSpan(); + + /// <summary> + /// Returns a handle to the memory that has been pinned and hence its address can be taken. + /// <param name="elementIndex">The offset to the element within the memory at which the returned <see cref="MemoryHandle"/> points to. (default = 0)</param> + /// </summary> + public abstract MemoryHandle Pin(int elementIndex = 0); + + /// <summary> + /// Lets the garbage collector know that the object is free to be moved now. + /// </summary> + public abstract void Unpin(); + + /// <summary> + /// Returns an array segment. + /// <remarks>Returns the default array segment if not overriden.</remarks> + /// </summary> + protected internal virtual bool TryGetArray(out ArraySegment<T> segment) + { + segment = default; + return false; + } + + /// <summary> + /// Implements IDisposable. + /// </summary> + void IDisposable.Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + /// <summary> + /// Clean up of any leftover managed and unmanaged resources. + /// </summary> + protected abstract void Dispose(bool disposing); + + } +} diff --git a/src/Common/src/CoreLib/System/Buffers/OwnedMemory.cs b/src/Common/src/CoreLib/System/Buffers/OwnedMemory.cs deleted file mode 100644 index 8acd5b224a..0000000000 --- a/src/Common/src/CoreLib/System/Buffers/OwnedMemory.cs +++ /dev/null @@ -1,95 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Runtime; -using System.Runtime.CompilerServices; - -namespace System.Buffers -{ - /// <summary> - /// Owner of Memory<typeparamref name="T"/> that provides appropriate lifetime management mechanisms for it. - /// </summary> - public abstract class OwnedMemory<T> : IDisposable, IRetainable - { - /// <summary> - /// The number of items in the Memory<typeparamref name="T"/>. - /// </summary> - public abstract int Length { get; } - - /// <summary> - /// Returns a span wrapping the underlying memory. - /// </summary> - public abstract Span<T> Span { get; } - - /// <summary> - /// Returns a Memory<typeparamref name="T"/> if the underlying memory has not been freed. - /// </summary> - /// <exception cref="System.ObjectDisposedException"> - /// Thrown when the underlying memory has already been disposed. - /// </exception> - public Memory<T> Memory - { - get - { - if (IsDisposed) - { - ThrowHelper.ThrowObjectDisposedException_MemoryDisposed(); - } - return new Memory<T>(owner: this, 0, Length); - } - } - - /// <summary> - /// Returns a handle for the array that has been pinned and hence its address can be taken - /// </summary> - public abstract MemoryHandle Pin(int byteOffset = 0); - - /// <summary> - /// Returns an array segment. - /// </summary> - protected internal abstract bool TryGetArray(out ArraySegment<T> segment); - - /// <summary> - /// Implements IDisposable. - /// </summary> - /// <exception cref="System.InvalidOperationException"> - /// Throw when there are still retained references to the memory - /// </exception> - public void Dispose() - { - if (IsRetained) - { - ThrowHelper.ThrowInvalidOperationException_OutstandingReferences(); - } - Dispose(true); - GC.SuppressFinalize(this); - } - - /// <summary> - /// Clean up of any leftover managed and unmanaged resources. - /// </summary> - protected abstract void Dispose(bool disposing); - - /// <summary> - /// Return true if someone is holding a reference to the memory. - /// </summary> - protected abstract bool IsRetained { get; } - - /// <summary> - /// Return true if the underlying memory has been freed. - /// </summary> - public abstract bool IsDisposed { get; } - - /// <summary> - /// Implements IRetainable. Prevent accidental disposal of the memory. - /// </summary> - public abstract void Retain(); - - /// <summary> - /// Implements IRetainable. The memory can now be diposed. - /// </summary> - public abstract bool Release(); - - } -} diff --git a/src/Common/src/CoreLib/System/Collections/Generic/Dictionary.cs b/src/Common/src/CoreLib/System/Collections/Generic/Dictionary.cs index d299af3ea7..ec72a8c780 100644 --- a/src/Common/src/CoreLib/System/Collections/Generic/Dictionary.cs +++ b/src/Common/src/CoreLib/System/Collections/Generic/Dictionary.cs @@ -339,7 +339,7 @@ namespace System.Collections.Generic { if (entries[i].hashCode >= 0) { - array[index + i] = new KeyValuePair<TKey, TValue>(entries[i].key, entries[i].value); + array[index++] = new KeyValuePair<TKey, TValue>(entries[i].key, entries[i].value); } } } @@ -923,7 +923,7 @@ namespace System.Collections.Generic { if (entries[i].hashCode >= 0) { - dictEntryArray[index + i] = new DictionaryEntry(entries[i].key, entries[i].value); + dictEntryArray[index++] = new DictionaryEntry(entries[i].key, entries[i].value); } } } @@ -943,7 +943,7 @@ namespace System.Collections.Generic { if (entries[i].hashCode >= 0) { - objects[index + i] = new KeyValuePair<TKey, TValue>(entries[i].key, entries[i].value); + objects[index++] = new KeyValuePair<TKey, TValue>(entries[i].key, entries[i].value); } } } @@ -1317,7 +1317,7 @@ namespace System.Collections.Generic Entry[] entries = _dictionary._entries; for (int i = 0; i < count; i++) { - if (entries[i].hashCode >= 0) array[index + i] = entries[i].key; + if (entries[i].hashCode >= 0) array[index++] = entries[i].key; } } @@ -1377,7 +1377,7 @@ namespace System.Collections.Generic { for (int i = 0; i < count; i++) { - if (entries[i].hashCode >= 0) objects[index + i] = entries[i].key; + if (entries[i].hashCode >= 0) objects[index++] = entries[i].key; } } catch (ArrayTypeMismatchException) @@ -1506,7 +1506,7 @@ namespace System.Collections.Generic Entry[] entries = _dictionary._entries; for (int i = 0; i < count; i++) { - if (entries[i].hashCode >= 0) array[index + i] = entries[i].value; + if (entries[i].hashCode >= 0) array[index++] = entries[i].value; } } @@ -1566,7 +1566,7 @@ namespace System.Collections.Generic { for (int i = 0; i < count; i++) { - if (entries[i].hashCode >= 0) objects[index + i] = entries[i].value; + if (entries[i].hashCode >= 0) objects[index++] = entries[i].value; } } catch (ArrayTypeMismatchException) diff --git a/src/Common/src/CoreLib/System/Collections/Generic/List.cs b/src/Common/src/CoreLib/System/Collections/Generic/List.cs index cc82302628..b8fd8eb5e2 100644 --- a/src/Common/src/CoreLib/System/Collections/Generic/List.cs +++ b/src/Common/src/CoreLib/System/Collections/Generic/List.cs @@ -166,12 +166,12 @@ namespace System.Collections.Generic set { - _version++; if ((uint)index >= (uint)_size) { ThrowHelper.ThrowArgumentOutOfRange_IndexException(); } _items[index] = value; + _version++; } } diff --git a/src/Common/src/CoreLib/System/Memory.cs b/src/Common/src/CoreLib/System/Memory.cs index e1316f8509..5c2f509fd2 100644 --- a/src/Common/src/CoreLib/System/Memory.cs +++ b/src/Common/src/CoreLib/System/Memory.cs @@ -28,15 +28,18 @@ namespace System // as code uses Unsafe.As to cast between them. // The highest order bit of _index is used to discern whether _object is an array/string or an owned memory - // if (_index >> 31) == 1, object _object is an OwnedMemory<T> - // else, object _object is a T[] or a string. It can only be a string if the Memory<T> was created by + // if (_index >> 31) == 1, object _object is an MemoryManager<T> + // else, object _object is a T[] or a string. + // if (_length >> 31) == 1, _object is a pre-pinned array, so Pin() will not allocate a new GCHandle + // else, Pin() needs to allocate a new GCHandle to pin the object. + // It can only be a string if the Memory<T> was created by // using unsafe / marshaling code to reinterpret a ReadOnlyMemory<char> wrapped around a string as // a Memory<T>. private readonly object _object; private readonly int _index; private readonly int _length; - private const int RemoveOwnedFlagBitMask = 0x7FFFFFFF; + private const int RemoveFlagsBitMask = 0x7FFFFFFF; /// <summary> /// Creates a new memory over the entirety of the target array. @@ -112,22 +115,72 @@ namespace System _length = length; } - // Constructor for internal use only. + /// <summary> + /// Creates a new memory from a memory manager that provides specific method implementations beginning + /// at 'start' index and ending at 'end' index (exclusive). + /// </summary> + /// <param name="manager">The memory manager.</param> + /// <param name="start">The index at which to begin the memory.</param> + /// <param name="length">The number of items in the memory.</param> + /// <remarks>Returns default when <paramref name="manager"/> is null.</remarks> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> or end index is not in the range (<0 or >=Length). + /// </exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Memory(OwnedMemory<T> owner, int index, int length) + public Memory(MemoryManager<T> manager, int start, int length) { - // No validation performed; caller must provide any necessary validation. - _object = owner; - _index = index | (1 << 31); // Before using _index, check if _index < 0, then 'and' it with RemoveOwnedFlagBitMask + if (manager == null) + { + if (start != 0 || length != 0) + ThrowHelper.ThrowArgumentOutOfRangeException(); + this = default; + return; // returns default + } + if ((uint)start > (uint)manager.Length || (uint)length > (uint)(manager.Length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + _object = manager; + _index = start | (1 << 31); // Before using _index, check if _index < 0, then 'and' it with RemoveFlagsBitMask _length = length; } + /// <summary> + /// Creates a new memory over the portion of the pre-pinned target array beginning + /// at 'start' index and ending at 'end' index (exclusive). + /// </summary> + /// <param name="array">The pre-pinned target array.</param> + /// <param name="start">The index at which to begin the memory.</param> + /// <param name="length">The number of items in the memory.</param> + /// <remarks>Returns default when <paramref name="array"/> is null.</remarks> + /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> or end index is not in the range (<0 or >=Length). + /// </exception> + [EditorBrowsable(EditorBrowsableState.Never)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Memory(object obj, int index, int length) + public static Memory<T> CreateFromPinnedArray(T[] array, int start, int length) + { + if (array == null) + { + if (start != 0 || length != 0) + ThrowHelper.ThrowArgumentOutOfRangeException(); + return default; + } + if (default(T) == null && array.GetType() != typeof(T[])) + ThrowHelper.ThrowArrayTypeMismatchException(); + if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + // Before using _length, check if _length < 0, then 'and' it with RemoveFlagsBitMask + return new Memory<T>((object)array, start, length | (1 << 31)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Memory(object obj, int start, int length) { // No validation performed; caller must provide any necessary validation. _object = obj; - _index = index; + _index = start; _length = length; } @@ -155,12 +208,12 @@ namespace System /// <summary> /// The number of items in the memory. /// </summary> - public int Length => _length; + public int Length => _length & RemoveFlagsBitMask; /// <summary> /// Returns true if Length is 0. /// </summary> - public bool IsEmpty => _length == 0; + public bool IsEmpty => (_length & RemoveFlagsBitMask) == 0; /// <summary> /// For <see cref="Memory{Char}"/>, returns a new instance of string that represents the characters pointed to by the memory. @@ -170,9 +223,9 @@ namespace System { if (typeof(T) == typeof(char)) { - return (_object is string str) ? str.Substring(_index, _length) : Span.ToString(); + return (_object is string str) ? str.Substring(_index, _length & RemoveFlagsBitMask) : Span.ToString(); } - return string.Format("System.Memory<{0}>[{1}]", typeof(T).Name, _length); + return string.Format("System.Memory<{0}>[{1}]", typeof(T).Name, _length & RemoveFlagsBitMask); } /// <summary> @@ -185,12 +238,13 @@ namespace System [MethodImpl(MethodImplOptions.AggressiveInlining)] public Memory<T> Slice(int start) { - if ((uint)start > (uint)_length) + int actualLength = _length & RemoveFlagsBitMask; + if ((uint)start > (uint)actualLength) { ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); } - return new Memory<T>(_object, _index + start, _length - start); + return new Memory<T>(_object, _index + start, actualLength - start); } /// <summary> @@ -204,7 +258,8 @@ namespace System [MethodImpl(MethodImplOptions.AggressiveInlining)] public Memory<T> Slice(int start, int length) { - if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start)) + int actualLength = _length & RemoveFlagsBitMask; + if ((uint)start > (uint)actualLength || (uint)length > (uint)(actualLength - start)) { ThrowHelper.ThrowArgumentOutOfRangeException(); } @@ -222,10 +277,12 @@ namespace System { if (_index < 0) { - return ((OwnedMemory<T>)_object).Span.Slice(_index & RemoveOwnedFlagBitMask, _length); + Debug.Assert(_length >= 0); + return ((MemoryManager<T>)_object).GetSpan().Slice(_index & RemoveFlagsBitMask, _length); } else if (typeof(T) == typeof(char) && _object is string s) { + Debug.Assert(_length >= 0); // This is dangerous, returning a writable span for a string that should be immutable. // However, we need to handle the case where a ReadOnlyMemory<char> was created from a string // and then cast to a Memory<T>. Such a cast can only be done with unsafe or marshaling code, @@ -239,7 +296,7 @@ namespace System } else if (_object != null) { - return new Span<T>((T[])_object, _index, _length); + return new Span<T>((T[])_object, _index, _length & RemoveFlagsBitMask); } else { @@ -280,7 +337,7 @@ namespace System { if (_index < 0) { - return ((OwnedMemory<T>)_object).Pin((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf<T>()); + return ((MemoryManager<T>)_object).Pin((_index & RemoveFlagsBitMask)); } else if (typeof(T) == typeof(char) && _object is string s) { @@ -295,72 +352,21 @@ namespace System #else void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref s.GetRawStringData()), _index); #endif // FEATURE_PORTABLE_SPAN - return new MemoryHandle(null, pointer, handle); + return new MemoryHandle(pointer, handle); } else if (_object is T[] array) { - var handle = GCHandle.Alloc(array, GCHandleType.Pinned); + GCHandle handle = _length < 0 ? default : GCHandle.Alloc(array, GCHandleType.Pinned); #if FEATURE_PORTABLE_SPAN void* pointer = Unsafe.Add<T>((void*)handle.AddrOfPinnedObject(), _index); #else void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index); #endif // FEATURE_PORTABLE_SPAN - return new MemoryHandle(null, pointer, handle); + return new MemoryHandle(pointer, handle); } return default; } - /// <summary>[Obsolete, use Pin()] Creates a handle for the memory.</summary> - /// <param name="pin"> - /// If pin is true, the GC will not move the array until the returned <see cref="MemoryHandle"/> - /// is disposed, enabling taking and using the memory's address. - /// </param> - public unsafe MemoryHandle Retain(bool pin = false) - { - MemoryHandle memoryHandle = default; - if (pin) - { - if (_index < 0) - { - memoryHandle = ((OwnedMemory<T>)_object).Pin((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf<T>()); - } - else if (typeof(T) == typeof(char) && _object is string s) - { - // This case can only happen if a ReadOnlyMemory<char> was created around a string - // and then that was cast to a Memory<char> using unsafe / marshaling code. This needs - // to work, however, so that code that uses a single Memory<char> field to store either - // a readable ReadOnlyMemory<char> or a writable Memory<char> can still be pinned and - // used for interop purposes. - GCHandle handle = GCHandle.Alloc(s, GCHandleType.Pinned); -#if FEATURE_PORTABLE_SPAN - void* pointer = Unsafe.Add<T>((void*)handle.AddrOfPinnedObject(), _index); -#else - void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref s.GetRawStringData()), _index); -#endif // FEATURE_PORTABLE_SPAN - memoryHandle = new MemoryHandle(null, pointer, handle); - } - else if (_object is T[] array) - { - var handle = GCHandle.Alloc(array, GCHandleType.Pinned); -#if FEATURE_PORTABLE_SPAN - void* pointer = Unsafe.Add<T>((void*)handle.AddrOfPinnedObject(), _index); -#else - void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index); -#endif // FEATURE_PORTABLE_SPAN - memoryHandle = new MemoryHandle(null, pointer, handle); - } - } - else - { - if (_index < 0) - { - ((OwnedMemory<T>)_object).Retain(); - memoryHandle = new MemoryHandle((OwnedMemory<T>)_object); - } - } - return memoryHandle; - } - /// <summary> /// Copies the contents from the memory into a new array. This heap /// allocates, so should generally be avoided, however it is sometimes diff --git a/src/Common/src/CoreLib/System/MemoryExtensions.Fast.cs b/src/Common/src/CoreLib/System/MemoryExtensions.Fast.cs index d9e3af8804..d256887a9f 100644 --- a/src/Common/src/CoreLib/System/MemoryExtensions.Fast.cs +++ b/src/Common/src/CoreLib/System/MemoryExtensions.Fast.cs @@ -359,6 +359,7 @@ namespace System /// <summary> /// Creates a new span over the portion of the target array. /// </summary> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span<T> AsSpan<T>(this T[] array, int start) { if (array == null) @@ -380,6 +381,7 @@ namespace System /// </summary> /// <param name="text">The target string.</param> /// <remarks>Returns default when <paramref name="text"/> is null.</remarks> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan<char> AsSpan(this string text) { if (text == null) @@ -397,6 +399,7 @@ namespace System /// <exception cref="System.ArgumentOutOfRangeException"> /// Thrown when the specified <paramref name="start"/> index is not in range (<0 or >text.Length). /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan<char> AsSpan(this string text, int start) { if (text == null) @@ -422,6 +425,7 @@ namespace System /// <exception cref="System.ArgumentOutOfRangeException"> /// Thrown when the specified <paramref name="start"/> index or <paramref name="length"/> is not in range. /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan<char> AsSpan(this string text, int start, int length) { if (text == null) diff --git a/src/Common/src/CoreLib/System/MemoryExtensions.cs b/src/Common/src/CoreLib/System/MemoryExtensions.cs index 1a3b33acc7..c3e1cd51fa 100644 --- a/src/Common/src/CoreLib/System/MemoryExtensions.cs +++ b/src/Common/src/CoreLib/System/MemoryExtensions.cs @@ -68,7 +68,6 @@ namespace System /// </summary> /// <param name="span">The source span from which the character is removed.</param> /// <param name="trimChar">The specified character to look for and remove.</param> - //[MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> span, char trimChar) { return span.TrimStart(trimChar).TrimEnd(trimChar); @@ -79,7 +78,6 @@ namespace System /// </summary> /// <param name="span">The source span from which the character is removed.</param> /// <param name="trimChar">The specified character to look for and remove.</param> - //[MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan<char> TrimStart(this ReadOnlySpan<char> span, char trimChar) { int start = 0; @@ -1204,7 +1202,7 @@ namespace System /// no larger element, the bitwise complement of <see cref="Span{T}.Length"/>. /// </returns> /// <exception cref="T:System.ArgumentNullException"> - /// <paramref name = "comparable" /> is <see langword="null"/> . + /// <paramref name = "comparable" /> is <see langword="null"/> . /// </exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int BinarySearch<T>( @@ -1228,7 +1226,7 @@ namespace System /// no larger element, the bitwise complement of <see cref="Span{T}.Length"/>. /// </returns> /// <exception cref="T:System.ArgumentNullException"> - /// <paramref name = "comparable" /> is <see langword="null"/> . + /// <paramref name = "comparable" /> is <see langword="null"/> . /// </exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int BinarySearch<T, TComparable>( @@ -1278,7 +1276,7 @@ namespace System /// no larger element, the bitwise complement of <see cref="ReadOnlySpan{T}.Length"/>. /// </returns> /// <exception cref="T:System.ArgumentNullException"> - /// <paramref name = "comparable" /> is <see langword="null"/> . + /// <paramref name = "comparable" /> is <see langword="null"/> . /// </exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int BinarySearch<T>( @@ -1302,7 +1300,7 @@ namespace System /// no larger element, the bitwise complement of <see cref="ReadOnlySpan{T}.Length"/>. /// </returns> /// <exception cref="T:System.ArgumentNullException"> - /// <paramref name = "comparable" /> is <see langword="null"/> . + /// <paramref name = "comparable" /> is <see langword="null"/> . /// </exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int BinarySearch<T, TComparable>( diff --git a/src/Common/src/CoreLib/System/ReadOnlyMemory.cs b/src/Common/src/CoreLib/System/ReadOnlyMemory.cs index 5385ceca76..c92f842062 100644 --- a/src/Common/src/CoreLib/System/ReadOnlyMemory.cs +++ b/src/Common/src/CoreLib/System/ReadOnlyMemory.cs @@ -28,13 +28,15 @@ namespace System // as code uses Unsafe.As to cast between them. // The highest order bit of _index is used to discern whether _object is an array/string or an owned memory - // if (_index >> 31) == 1, _object is an OwnedMemory<T> - // else, _object is a T[] or string + // if (_index >> 31) == 1, _object is an MemoryManager<T> + // else, _object is a T[] or string. + // if (_length >> 31) == 1, _object is a pre-pinned array, so Pin() will not allocate a new GCHandle + // else, Pin() needs to allocate a new GCHandle to pin the object. private readonly object _object; private readonly int _index; private readonly int _length; - internal const int RemoveOwnedFlagBitMask = 0x7FFFFFFF; + internal const int RemoveFlagsBitMask = 0x7FFFFFFF; /// <summary> /// Creates a new memory over the entirety of the target array. @@ -117,12 +119,12 @@ namespace System /// <summary> /// The number of items in the memory. /// </summary> - public int Length => _length; + public int Length => _length & RemoveFlagsBitMask; /// <summary> /// Returns true if Length is 0. /// </summary> - public bool IsEmpty => _length == 0; + public bool IsEmpty => (_length & RemoveFlagsBitMask) == 0; /// <summary> /// For <see cref="ReadOnlyMemory{Char}"/>, returns a new instance of string that represents the characters pointed to by the memory. @@ -132,9 +134,9 @@ namespace System { if (typeof(T) == typeof(char)) { - return (_object is string str) ? str.Substring(_index, _length) : Span.ToString(); + return (_object is string str) ? str.Substring(_index, _length & RemoveFlagsBitMask) : Span.ToString(); } - return string.Format("System.ReadOnlyMemory<{0}>[{1}]", typeof(T).Name, _length); + return string.Format("System.ReadOnlyMemory<{0}>[{1}]", typeof(T).Name, _length & RemoveFlagsBitMask); } /// <summary> @@ -147,12 +149,13 @@ namespace System [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlyMemory<T> Slice(int start) { - if ((uint)start > (uint)_length) + int actualLength = _length & RemoveFlagsBitMask; + if ((uint)start > (uint)actualLength) { ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); } - return new ReadOnlyMemory<T>(_object, _index + start, _length - start); + return new ReadOnlyMemory<T>(_object, _index + start, actualLength - start); } /// <summary> @@ -166,7 +169,8 @@ namespace System [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlyMemory<T> Slice(int start, int length) { - if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start)) + int actualLength = _length & RemoveFlagsBitMask; + if ((uint)start > (uint)actualLength || (uint)length > (uint)(actualLength - start)) { ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); } @@ -184,10 +188,12 @@ namespace System { if (_index < 0) { - return ((OwnedMemory<T>)_object).Span.Slice(_index & RemoveOwnedFlagBitMask, _length); + Debug.Assert(_length >= 0); + return ((MemoryManager<T>)_object).GetSpan().Slice(_index & RemoveFlagsBitMask, _length); } else if (typeof(T) == typeof(char) && _object is string s) { + Debug.Assert(_length >= 0); #if FEATURE_PORTABLE_SPAN return new ReadOnlySpan<T>(Unsafe.As<Pinnable<T>>(s), MemoryExtensions.StringAdjustment, s.Length).Slice(_index, _length); #else @@ -196,7 +202,7 @@ namespace System } else if (_object != null) { - return new ReadOnlySpan<T>((T[])_object, _index, _length); + return new ReadOnlySpan<T>((T[])_object, _index, _length & RemoveFlagsBitMask); } else { @@ -237,7 +243,7 @@ namespace System { if (_index < 0) { - return ((OwnedMemory<T>)_object).Pin((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf<T>()); + return ((MemoryManager<T>)_object).Pin((_index & RemoveFlagsBitMask)); } else if (typeof(T) == typeof(char) && _object is string s) { @@ -247,67 +253,21 @@ namespace System #else void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref s.GetRawStringData()), _index); #endif // FEATURE_PORTABLE_SPAN - return new MemoryHandle(null, pointer, handle); + return new MemoryHandle(pointer, handle); } else if (_object is T[] array) { - var handle = GCHandle.Alloc(array, GCHandleType.Pinned); + GCHandle handle = _length < 0 ? default : GCHandle.Alloc(array, GCHandleType.Pinned); #if FEATURE_PORTABLE_SPAN void* pointer = Unsafe.Add<T>((void*)handle.AddrOfPinnedObject(), _index); #else void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index); #endif // FEATURE_PORTABLE_SPAN - return new MemoryHandle(null, pointer, handle); + return new MemoryHandle(pointer, handle); } return default; } - /// <summary>[Obsolete, use Pin()] Creates a handle for the memory.</summary> - /// <param name="pin"> - /// If pin is true, the GC will not move the array until the returned <see cref="MemoryHandle"/> - /// is disposed, enabling taking and using the memory's address. - /// </param> - public unsafe MemoryHandle Retain(bool pin = false) - { - MemoryHandle memoryHandle = default; - if (pin) - { - if (_index < 0) - { - memoryHandle = ((OwnedMemory<T>)_object).Pin((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf<T>()); - } - else if (typeof(T) == typeof(char) && _object is string s) - { - GCHandle handle = GCHandle.Alloc(s, GCHandleType.Pinned); -#if FEATURE_PORTABLE_SPAN - void* pointer = Unsafe.Add<T>((void*)handle.AddrOfPinnedObject(), _index); -#else - void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref s.GetRawStringData()), _index); -#endif // FEATURE_PORTABLE_SPAN - memoryHandle = new MemoryHandle(null, pointer, handle); - } - else if (_object is T[] array) - { - var handle = GCHandle.Alloc(array, GCHandleType.Pinned); -#if FEATURE_PORTABLE_SPAN - void* pointer = Unsafe.Add<T>((void*)handle.AddrOfPinnedObject(), _index); -#else - void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index); -#endif // FEATURE_PORTABLE_SPAN - memoryHandle = new MemoryHandle(null, pointer, handle); - } - } - else - { - if (_index < 0) - { - ((OwnedMemory<T>)_object).Retain(); - memoryHandle = new MemoryHandle((OwnedMemory<T>)_object); - } - } - return memoryHandle; - } - /// <summary> /// Copies the contents from the memory into a new array. This heap /// allocates, so should generally be avoided, however it is sometimes diff --git a/src/Common/src/CoreLib/System/Runtime/InteropServices/MemoryMarshal.cs b/src/Common/src/CoreLib/System/Runtime/InteropServices/MemoryMarshal.cs index b4b17b0370..073dd76cdc 100644 --- a/src/Common/src/CoreLib/System/Runtime/InteropServices/MemoryMarshal.cs +++ b/src/Common/src/CoreLib/System/Runtime/InteropServices/MemoryMarshal.cs @@ -5,6 +5,7 @@ using System.Buffers; using System.Runtime.CompilerServices; using System.Collections.Generic; +using System.Diagnostics; #if !netstandard using Internal.Runtime.CompilerServices; @@ -27,19 +28,20 @@ namespace System.Runtime.InteropServices object obj = memory.GetObjectStartLength(out int index, out int length); if (index < 0) { - if (((OwnedMemory<T>)obj).TryGetArray(out ArraySegment<T> arraySegment)) + Debug.Assert(length >= 0); + if (((MemoryManager<T>)obj).TryGetArray(out ArraySegment<T> arraySegment)) { - segment = new ArraySegment<T>(arraySegment.Array, arraySegment.Offset + (index & ReadOnlyMemory<T>.RemoveOwnedFlagBitMask), length); + segment = new ArraySegment<T>(arraySegment.Array, arraySegment.Offset + (index & ReadOnlyMemory<T>.RemoveFlagsBitMask), length); return true; } } else if (obj is T[] arr) { - segment = new ArraySegment<T>(arr, index, length); + segment = new ArraySegment<T>(arr, index, length & ReadOnlyMemory<T>.RemoveFlagsBitMask); return true; } - if (length == 0) + if ((length & ReadOnlyMemory<T>.RemoveFlagsBitMask) == 0) { #if FEATURE_PORTABLE_SPAN segment = new ArraySegment<T>(SpanHelpers.PerTypeValues<T>.EmptyArray); @@ -54,40 +56,49 @@ namespace System.Runtime.InteropServices } /// <summary> - /// Gets an <see cref="OwnedMemory{T}"/> from the underlying read-only memory. - /// If unable to get the <typeparamref name="TOwner"/> type, returns false. + /// Gets an <see cref="MemoryManager{T}"/> from the underlying read-only memory. + /// If unable to get the <typeparamref name="TManager"/> type, returns false. /// </summary> /// <typeparam name="T">The element type of the <paramref name="memory" />.</typeparam> - /// <typeparam name="TOwner">The type of <see cref="OwnedMemory{T}"/> to try and retrive.</typeparam> - /// <param name="memory">The memory to get the owner for.</param> - /// <param name="owner">The returned owner of the <see cref="ReadOnlyMemory{T}"/>.</param> + /// <typeparam name="TManager">The type of <see cref="MemoryManager{T}"/> to try and retrive.</typeparam> + /// <param name="memory">The memory to get the manager for.</param> + /// <param name="manager">The returned manager of the <see cref="ReadOnlyMemory{T}"/>.</param> /// <returns>A <see cref="bool"/> indicating if it was successful.</returns> - public static bool TryGetOwnedMemory<T, TOwner>(ReadOnlyMemory<T> memory, out TOwner owner) - where TOwner : OwnedMemory<T> + public static bool TryGetMemoryManager<T, TManager>(ReadOnlyMemory<T> memory, out TManager manager) + where TManager : MemoryManager<T> { - TOwner localOwner; // Use register for null comparison rather than byref - owner = localOwner = memory.GetObjectStartLength(out int index, out int length) as TOwner; - return !ReferenceEquals(owner, null); + TManager localManager; // Use register for null comparison rather than byref + manager = localManager = memory.GetObjectStartLength(out _, out _) as TManager; + return !ReferenceEquals(manager, null); } /// <summary> - /// Gets an <see cref="OwnedMemory{T}"/> and <paramref name="start" />, <paramref name="length" /> from the underlying read-only memory. - /// If unable to get the <typeparamref name="TOwner"/> type, returns false. + /// Gets an <see cref="MemoryManager{T}"/> and <paramref name="start" />, <paramref name="length" /> from the underlying read-only memory. + /// If unable to get the <typeparamref name="TManager"/> type, returns false. /// </summary> /// <typeparam name="T">The element type of the <paramref name="memory" />.</typeparam> - /// <typeparam name="TOwner">The type of <see cref="OwnedMemory{T}"/> to try and retrive.</typeparam> - /// <param name="memory">The memory to get the owner for.</param> - /// <param name="owner">The returned owner of the <see cref="ReadOnlyMemory{T}"/>.</param> - /// <param name="start">The offset from the start of the <paramref name="owner" /> that the <paramref name="memory" /> represents.</param> - /// <param name="length">The length of the <paramref name="owner" /> that the <paramref name="memory" /> represents.</param> + /// <typeparam name="TManager">The type of <see cref="MemoryManager{T}"/> to try and retrive.</typeparam> + /// <param name="memory">The memory to get the manager for.</param> + /// <param name="manager">The returned manager of the <see cref="ReadOnlyMemory{T}"/>.</param> + /// <param name="start">The offset from the start of the <paramref name="manager" /> that the <paramref name="memory" /> represents.</param> + /// <param name="length">The length of the <paramref name="manager" /> that the <paramref name="memory" /> represents.</param> /// <returns>A <see cref="bool"/> indicating if it was successful.</returns> - public static bool TryGetOwnedMemory<T, TOwner>(ReadOnlyMemory<T> memory, out TOwner owner, out int start, out int length) - where TOwner : OwnedMemory<T> + public static bool TryGetMemoryManager<T, TManager>(ReadOnlyMemory<T> memory, out TManager manager, out int start, out int length) + where TManager : MemoryManager<T> { - TOwner localOwner; // Use register for null comparison rather than byref - owner = localOwner = memory.GetObjectStartLength(out start, out length) as TOwner; - start &= ReadOnlyMemory<T>.RemoveOwnedFlagBitMask; - return !ReferenceEquals(owner, null); + TManager localManager; // Use register for null comparison rather than byref + manager = localManager = memory.GetObjectStartLength(out start, out length) as TManager; + start &= ReadOnlyMemory<T>.RemoveFlagsBitMask; + + Debug.Assert(length >= 0); + + if (ReferenceEquals(manager, null)) + { + start = default; + length = default; + return false; + } + return true; } /// <summary> @@ -113,6 +124,8 @@ namespace System.Runtime.InteropServices { if (memory.GetObjectStartLength(out int offset, out int count) is string s) { + Debug.Assert(offset >= 0); + Debug.Assert(count >= 0); text = s; start = offset; length = count; diff --git a/src/Common/src/CoreLib/System/SpanHelpers.Char.cs b/src/Common/src/CoreLib/System/SpanHelpers.Char.cs index 2033d8b906..47e83b7b26 100644 --- a/src/Common/src/CoreLib/System/SpanHelpers.Char.cs +++ b/src/Common/src/CoreLib/System/SpanHelpers.Char.cs @@ -84,79 +84,87 @@ namespace System { Debug.Assert(length >= 0); - uint uValue = value; // Use uint for comparisons to avoid unnecessary 8->32 extensions - IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations - IntPtr nLength = (IntPtr)length; -#if !netstandard11 - if (Vector.IsHardwareAccelerated && length >= Vector<ushort>.Count * 2) + fixed (char* pChars = &searchSpace) { - const int elementsPerByte = sizeof(ushort) / sizeof(byte); - int unaligned = ((int)Unsafe.AsPointer(ref searchSpace) & (Vector<byte>.Count - 1)) / elementsPerByte; - nLength = (IntPtr)((Vector<ushort>.Count - unaligned) & (Vector<ushort>.Count - 1)); - } - SequentialScan: + char* pCh = pChars; + char* pEndCh = pCh + length; + +#if !netstandard11 + if (Vector.IsHardwareAccelerated && length >= Vector<ushort>.Count * 2) + { + const int elementsPerByte = sizeof(ushort) / sizeof(byte); + int unaligned = ((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) / elementsPerByte; + length = ((Vector<ushort>.Count - unaligned) & (Vector<ushort>.Count - 1)); + } + SequentialScan: #endif - while ((byte*)nLength >= (byte*)4) - { - nLength -= 4; - - if (uValue == Unsafe.Add(ref searchSpace, index)) - goto Found; - if (uValue == Unsafe.Add(ref searchSpace, index + 1)) - goto Found1; - if (uValue == Unsafe.Add(ref searchSpace, index + 2)) - goto Found2; - if (uValue == Unsafe.Add(ref searchSpace, index + 3)) - goto Found3; - - index += 4; - } + while (length >= 4) + { + length -= 4; + + if (*pCh == value) + goto Found; + if (*(pCh + 1) == value) + goto Found1; + if (*(pCh + 2) == value) + goto Found2; + if (*(pCh + 3) == value) + goto Found3; + + pCh += 4; + } - while ((byte*)nLength > (byte*)0) - { - nLength -= 1; + while (length > 0) + { + length -= 1; - if (uValue == Unsafe.Add(ref searchSpace, index)) - goto Found; + if (*pCh == value) + goto Found; - index += 1; - } + pCh += 1; + } #if !netstandard11 - if (Vector.IsHardwareAccelerated && ((int)(byte*)index < length)) - { - nLength = (IntPtr)((length - (int)(byte*)index) & ~(Vector<ushort>.Count - 1)); + // We get past SequentialScan only if IsHardwareAccelerated is true. However, we still have the redundant check to allow + // the JIT to see that the code is unreachable and eliminate it when the platform does not have hardware accelerated. + if (Vector.IsHardwareAccelerated && pCh < pEndCh) + { + length = (int)((pEndCh - pCh) & ~(Vector<ushort>.Count - 1)); - // Get comparison Vector - Vector<ushort> vComparison = new Vector<ushort>(value); + // Get comparison Vector + Vector<ushort> vComparison = new Vector<ushort>(value); - while ((byte*)nLength > (byte*)index) - { - var vMatches = Vector.Equals(vComparison, Unsafe.ReadUnaligned<Vector<ushort>>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref searchSpace, index)))); - if (Vector<ushort>.Zero.Equals(vMatches)) + while (length > 0) { - index += Vector<ushort>.Count; - continue; + // Using Unsafe.Read instead of ReadUnaligned since the search space is pinned and pCh is always vector aligned + Debug.Assert(((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) == 0); + Vector<ushort> vMatches = Vector.Equals(vComparison, Unsafe.Read<Vector<ushort>>(pCh)); + if (Vector<ushort>.Zero.Equals(vMatches)) + { + pCh += Vector<ushort>.Count; + length -= Vector<ushort>.Count; + continue; + } + // Find offset of first match + return (int)(pCh - pChars) + LocateFirstFoundChar(vMatches); } - // Find offset of first match - return (int)(byte*)index + LocateFirstFoundChar(vMatches); - } - if ((int)(byte*)index < length) - { - nLength = (IntPtr)(length - (int)(byte*)index); - goto SequentialScan; + if (pCh < pEndCh) + { + length = (int)(pEndCh - pCh); + goto SequentialScan; + } } - } #endif - return -1; - Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549 - return (int)(byte*)index; - Found1: - return (int)(byte*)(index + 1); - Found2: - return (int)(byte*)(index + 2); - Found3: - return (int)(byte*)(index + 3); + return -1; + Found3: + pCh++; + Found2: + pCh++; + Found1: + pCh++; + Found: + return (int)(pCh - pChars); + } } #if !netstandard11 diff --git a/src/Common/src/CoreLib/System/String.Searching.cs b/src/Common/src/CoreLib/System/String.Searching.cs index cc6e218d8d..a4e5bbb62e 100644 --- a/src/Common/src/CoreLib/System/String.Searching.cs +++ b/src/Common/src/CoreLib/System/String.Searching.cs @@ -35,10 +35,7 @@ namespace System // Returns the index of the first occurrence of a specified character in the current instance. // The search starts at startIndex and runs thorough the next count characters. // - public int IndexOf(char value) - { - return IndexOf(value, 0, this.Length); - } + public int IndexOf(char value) => SpanHelpers.IndexOf(ref _firstChar, value, Length); public int IndexOf(char value, int startIndex) { @@ -74,122 +71,19 @@ namespace System public unsafe int IndexOf(char value, int startIndex, int count) { + // These bounds checks are redundant since AsSpan does them already, but are required + // so that the correct parameter name is thrown as part of the exception. if ((uint)startIndex > (uint)Length) throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); if ((uint)count > (uint)(Length - startIndex)) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count); - fixed (char* pChars = &_firstChar) - { - char* pCh = pChars + startIndex; - char* pEndCh = pCh + count; - - if (Vector.IsHardwareAccelerated && count >= Vector<ushort>.Count * 2) - { - unchecked - { - const int elementsPerByte = sizeof(ushort) / sizeof(byte); - int unaligned = ((int)pCh & (Vector<byte>.Count - 1)) / elementsPerByte; - count = ((Vector<ushort>.Count - unaligned) & (Vector<ushort>.Count - 1)); - } - } - SequentialScan: - while (count >= 4) - { - if (*pCh == value) goto ReturnIndex; - if (*(pCh + 1) == value) goto ReturnIndex1; - if (*(pCh + 2) == value) goto ReturnIndex2; - if (*(pCh + 3) == value) goto ReturnIndex3; - - count -= 4; - pCh += 4; - } - - while (count > 0) - { - if (*pCh == value) - goto ReturnIndex; - - count--; - pCh++; - } - - if (pCh < pEndCh) - { - count = (int)((pEndCh - pCh) & ~(Vector<ushort>.Count - 1)); - // Get comparison Vector - Vector<ushort> vComparison = new Vector<ushort>(value); - while (count > 0) - { - var vMatches = Vector.Equals(vComparison, Unsafe.ReadUnaligned<Vector<ushort>>(pCh)); - if (Vector<ushort>.Zero.Equals(vMatches)) - { - pCh += Vector<ushort>.Count; - count -= Vector<ushort>.Count; - continue; - } - // Find offset of first match - return (int)(pCh - pChars) + LocateFirstFoundChar(vMatches); - } - - if (pCh < pEndCh) - { - unchecked - { - count = (int)(pEndCh - pCh); - } - goto SequentialScan; - } - } - - return -1; + int result = SpanHelpers.IndexOf(ref Unsafe.Add(ref _firstChar, startIndex), value, count); - ReturnIndex3: pCh++; - ReturnIndex2: pCh++; - ReturnIndex1: pCh++; - ReturnIndex: - return (int)(pCh - pChars); - } + return result == -1 ? result : result + startIndex; } - // Vector sub-search adapted from https://github.com/aspnet/KestrelHttpServer/pull/1138 - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int LocateFirstFoundChar(Vector<ushort> match) - { - var vector64 = Vector.AsVectorUInt64(match); - ulong candidate = 0; - int i = 0; - // Pattern unrolled by jit https://github.com/dotnet/coreclr/pull/8001 - for (; i < Vector<ulong>.Count; i++) - { - candidate = vector64[i]; - if (candidate != 0) - { - break; - } - } - - // Single LEA instruction with jitted const (using function result) - return i * 4 + LocateFirstFoundChar(candidate); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int LocateFirstFoundChar(ulong match) - { - unchecked - { - // Flag least significant power of two bit - var powerOfTwoFlag = match ^ (match - 1); - // Shift all powers of two into the high byte and extract - return (int)((powerOfTwoFlag * XorPowerOfTwoToHighChar) >> 49); - } - } - - private const ulong XorPowerOfTwoToHighChar = (0x03ul | - 0x02ul << 16 | - 0x01ul << 32) + 1; - // Returns the index of the first occurrence of any specified character in the current instance. // The search starts at startIndex and runs to startIndex + count - 1. // diff --git a/src/Common/src/CoreLib/System/String.cs b/src/Common/src/CoreLib/System/String.cs index a1251d6be0..42d21ba068 100644 --- a/src/Common/src/CoreLib/System/String.cs +++ b/src/Common/src/CoreLib/System/String.cs @@ -360,6 +360,7 @@ namespace System return result; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan<char>(string value) => value != null ? new ReadOnlySpan<char>(ref value.GetRawStringData(), value.Length) : default; diff --git a/src/Common/src/Interop/Unix/System.Net.Http.Native/Interop.Easy.cs b/src/Common/src/Interop/Unix/System.Net.Http.Native/Interop.Easy.cs index 5f6c576883..ab3200b333 100644 --- a/src/Common/src/Interop/Unix/System.Net.Http.Native/Interop.Easy.cs +++ b/src/Common/src/Interop/Unix/System.Net.Http.Native/Interop.Easy.cs @@ -161,6 +161,8 @@ internal static partial class Interop internal enum CurlSslVersion { CURL_SSLVERSION_TLSv1 = 1, /* TLS 1.x */ + CURL_SSLVERSION_SSLv2 = 2, /* SSL 2 */ + CURL_SSLVERSION_SSLv3 = 3, /* SSL 3 */ CURL_SSLVERSION_TLSv1_0 = 4, /* TLS 1.0 */ CURL_SSLVERSION_TLSv1_1 = 5, /* TLS 1.1 */ CURL_SSLVERSION_TLSv1_2 = 6, /* TLS 1.2 */ diff --git a/src/Common/src/Interop/Windows/SChannel/Interop.SchProtocols.cs b/src/Common/src/Interop/Windows/SChannel/Interop.SchProtocols.cs index a5185d3feb..c94bd92383 100644 --- a/src/Common/src/Interop/Windows/SChannel/Interop.SchProtocols.cs +++ b/src/Common/src/Interop/Windows/SChannel/Interop.SchProtocols.cs @@ -9,8 +9,6 @@ internal static partial class Interop // Most constants below are taken from schannel.h; those that are not are // called out explicitly. - // IMPORTANT: SSL2 and SSL3 definitions are required for System.Net.Primitives enum definitions only. - // These values should NOT be used in Schannel setup. public const int SP_PROT_SSL2_SERVER = 0x00000004; public const int SP_PROT_SSL2_CLIENT = 0x00000008; public const int SP_PROT_SSL2 = (SP_PROT_SSL2_SERVER | SP_PROT_SSL2_CLIENT); @@ -34,7 +32,7 @@ internal static partial class Interop public const int SP_PROT_NONE = 0; // These two constants are not taken from schannel.h. - public const int ClientProtocolMask = (SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT); - public const int ServerProtocolMask = (SP_PROT_TLS1_0_SERVER | SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_2_SERVER); + public const int ClientProtocolMask = (SP_PROT_SSL2_CLIENT | SP_PROT_SSL3_CLIENT | SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT); + public const int ServerProtocolMask = (SP_PROT_SSL2_SERVER | SP_PROT_SSL3_SERVER | SP_PROT_TLS1_0_SERVER | SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_2_SERVER); } } diff --git a/src/Common/src/System/Collections/Concurrent/ConcurrentQueue_Segment.cs b/src/Common/src/System/Collections/Concurrent/ConcurrentQueue_Segment.cs new file mode 100644 index 0000000000..a85817776c --- /dev/null +++ b/src/Common/src/System/Collections/Concurrent/ConcurrentQueue_Segment.cs @@ -0,0 +1,335 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; + +namespace System.Collections.Concurrent +{ + partial class ConcurrentQueue<T> + { + /// <summary> + /// Provides a multi-producer, multi-consumer thread-safe bounded segment. When the queue is full, + /// enqueues fail and return false. When the queue is empty, dequeues fail and return null. + /// These segments are linked together to form the unbounded <see cref="ConcurrentQueue{T}"/>. + /// </summary> + [DebuggerDisplay("Capacity = {Capacity}")] + internal sealed class Segment + { + // Segment design is inspired by the algorithm outlined at: + // http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue + + /// <summary>The array of items in this queue. Each slot contains the item in that slot and its "sequence number".</summary> + internal readonly Slot[] _slots; + /// <summary>Mask for quickly accessing a position within the queue's array.</summary> + internal readonly int _slotsMask; + /// <summary>The head and tail positions, with padding to help avoid false sharing contention.</summary> + /// <remarks>Dequeuing happens from the head, enqueuing happens at the tail.</remarks> + internal PaddedHeadAndTail _headAndTail; // mutable struct: do not make this readonly + + /// <summary>Indicates whether the segment has been marked such that dequeues don't overwrite the removed data.</summary> + internal bool _preservedForObservation; + /// <summary>Indicates whether the segment has been marked such that no additional items may be enqueued.</summary> + internal bool _frozenForEnqueues; +#pragma warning disable 0649 // some builds don't assign to this field + /// <summary>The segment following this one in the queue, or null if this segment is the last in the queue.</summary> + internal Segment _nextSegment; +#pragma warning restore 0649 + + /// <summary>Creates the segment.</summary> + /// <param name="boundedLength"> + /// The maximum number of elements the segment can contain. Must be a power of 2. + /// </param> + public Segment(int boundedLength) + { + // Validate the length + Debug.Assert(boundedLength >= 2, $"Must be >= 2, got {boundedLength}"); + Debug.Assert((boundedLength & (boundedLength - 1)) == 0, $"Must be a power of 2, got {boundedLength}"); + + // Initialize the slots and the mask. The mask is used as a way of quickly doing "% _slots.Length", + // instead letting us do "& _slotsMask". + _slots = new Slot[boundedLength]; + _slotsMask = boundedLength - 1; + + // Initialize the sequence number for each slot. The sequence number provides a ticket that + // allows dequeuers to know whether they can dequeue and enqueuers to know whether they can + // enqueue. An enqueuer at position N can enqueue when the sequence number is N, and a dequeuer + // for position N can dequeue when the sequence number is N + 1. When an enqueuer is done writing + // at position N, it sets the sequence number to N + 1 so that a dequeuer will be able to dequeue, + // and when a dequeuer is done dequeueing at position N, it sets the sequence number to N + _slots.Length, + // so that when an enqueuer loops around the slots, it'll find that the sequence number at + // position N is N. This also means that when an enqueuer finds that at position N the sequence + // number is < N, there is still a value in that slot, i.e. the segment is full, and when a + // dequeuer finds that the value in a slot is < N + 1, there is nothing currently available to + // dequeue. (It is possible for multiple enqueuers to enqueue concurrently, writing into + // subsequent slots, and to have the first enqueuer take longer, so that the slots for 1, 2, 3, etc. + // may have values, but the 0th slot may still be being filled... in that case, TryDequeue will + // return false.) + for (int i = 0; i < _slots.Length; i++) + { + _slots[i].SequenceNumber = i; + } + } + + /// <summary>Round the specified value up to the next power of 2, if it isn't one already.</summary> + internal static int RoundUpToPowerOf2(int i) + { + // Based on https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + --i; + i |= i >> 1; + i |= i >> 2; + i |= i >> 4; + i |= i >> 8; + i |= i >> 16; + return i + 1; + } + + /// <summary>Gets the number of elements this segment can store.</summary> + internal int Capacity => _slots.Length; + + /// <summary>Gets the "freeze offset" for this segment.</summary> + internal int FreezeOffset => _slots.Length * 2; + + /// <summary> + /// Ensures that the segment will not accept any subsequent enqueues that aren't already underway. + /// </summary> + /// <remarks> + /// When we mark a segment as being frozen for additional enqueues, + /// we set the <see cref="_frozenForEnqueues"/> bool, but that's mostly + /// as a small helper to avoid marking it twice. The real marking comes + /// by modifying the Tail for the segment, increasing it by this + /// <see cref="FreezeOffset"/>. This effectively knocks it off the + /// sequence expected by future enqueuers, such that any additional enqueuer + /// will be unable to enqueue due to it not lining up with the expected + /// sequence numbers. This value is chosen specially so that Tail will grow + /// to a value that maps to the same slot but that won't be confused with + /// any other enqueue/dequeue sequence number. + /// </remarks> + internal void EnsureFrozenForEnqueues() // must only be called while queue's segment lock is held + { + if (!_frozenForEnqueues) // flag used to ensure we don't increase the Tail more than once if frozen more than once + { + _frozenForEnqueues = true; + + // Increase the tail by FreezeOffset, spinning until we're successful in doing so. + var spinner = new SpinWait(); + while (true) + { + int tail = Volatile.Read(ref _headAndTail.Tail); + if (Interlocked.CompareExchange(ref _headAndTail.Tail, tail + FreezeOffset, tail) == tail) + { + break; + } + spinner.SpinOnce(); + } + } + } + + /// <summary>Tries to dequeue an element from the queue.</summary> + public bool TryDequeue(out T item) + { + // Loop in case of contention... + var spinner = new SpinWait(); + while (true) + { + // Get the head at which to try to dequeue. + int currentHead = Volatile.Read(ref _headAndTail.Head); + int slotsIndex = currentHead & _slotsMask; + + // Read the sequence number for the head position. + int sequenceNumber = Volatile.Read(ref _slots[slotsIndex].SequenceNumber); + + // We can dequeue from this slot if it's been filled by an enqueuer, which + // would have left the sequence number at pos+1. + int diff = sequenceNumber - (currentHead + 1); + if (diff == 0) + { + // We may be racing with other dequeuers. Try to reserve the slot by incrementing + // the head. Once we've done that, no one else will be able to read from this slot, + // and no enqueuer will be able to read from this slot until we've written the new + // sequence number. WARNING: The next few lines are not reliable on a runtime that + // supports thread aborts. If a thread abort were to sneak in after the CompareExchange + // but before the Volatile.Write, enqueuers trying to enqueue into this slot would + // spin indefinitely. If this implementation is ever used on such a platform, this + // if block should be wrapped in a finally / prepared region. + if (Interlocked.CompareExchange(ref _headAndTail.Head, currentHead + 1, currentHead) == currentHead) + { + // Successfully reserved the slot. Note that after the above CompareExchange, other threads + // trying to dequeue from this slot will end up spinning until we do the subsequent Write. + item = _slots[slotsIndex].Item; + if (!Volatile.Read(ref _preservedForObservation)) + { + // If we're preserving, though, we don't zero out the slot, as we need it for + // enumerations, peeking, ToArray, etc. And we don't update the sequence number, + // so that an enqueuer will see it as full and be forced to move to a new segment. + _slots[slotsIndex].Item = default(T); + Volatile.Write(ref _slots[slotsIndex].SequenceNumber, currentHead + _slots.Length); + } + return true; + } + } + else if (diff < 0) + { + // The sequence number was less than what we needed, which means this slot doesn't + // yet contain a value we can dequeue, i.e. the segment is empty. Technically it's + // possible that multiple enqueuers could have written concurrently, with those + // getting later slots actually finishing first, so there could be elements after + // this one that are available, but we need to dequeue in order. So before declaring + // failure and that the segment is empty, we check the tail to see if we're actually + // empty or if we're just waiting for items in flight or after this one to become available. + bool frozen = _frozenForEnqueues; + int currentTail = Volatile.Read(ref _headAndTail.Tail); + if (currentTail - currentHead <= 0 || (frozen && (currentTail - FreezeOffset - currentHead <= 0))) + { + item = default(T); + return false; + } + + // It's possible it could have become frozen after we checked _frozenForEnqueues + // and before reading the tail. That's ok: in that rare race condition, we just + // loop around again. + } + + // Lost a race. Spin a bit, then try again. + spinner.SpinOnce(); + } + } + + /// <summary>Tries to peek at an element from the queue, without removing it.</summary> + public bool TryPeek(out T result, bool resultUsed) + { + if (resultUsed) + { + // In order to ensure we don't get a torn read on the value, we mark the segment + // as preserving for observation. Additional items can still be enqueued to this + // segment, but no space will be freed during dequeues, such that the segment will + // no longer be reusable. + _preservedForObservation = true; + Interlocked.MemoryBarrier(); + } + + // Loop in case of contention... + var spinner = new SpinWait(); + while (true) + { + // Get the head at which to try to peek. + int currentHead = Volatile.Read(ref _headAndTail.Head); + int slotsIndex = currentHead & _slotsMask; + + // Read the sequence number for the head position. + int sequenceNumber = Volatile.Read(ref _slots[slotsIndex].SequenceNumber); + + // We can peek from this slot if it's been filled by an enqueuer, which + // would have left the sequence number at pos+1. + int diff = sequenceNumber - (currentHead + 1); + if (diff == 0) + { + result = resultUsed ? _slots[slotsIndex].Item : default(T); + return true; + } + else if (diff < 0) + { + // The sequence number was less than what we needed, which means this slot doesn't + // yet contain a value we can peek, i.e. the segment is empty. Technically it's + // possible that multiple enqueuers could have written concurrently, with those + // getting later slots actually finishing first, so there could be elements after + // this one that are available, but we need to peek in order. So before declaring + // failure and that the segment is empty, we check the tail to see if we're actually + // empty or if we're just waiting for items in flight or after this one to become available. + bool frozen = _frozenForEnqueues; + int currentTail = Volatile.Read(ref _headAndTail.Tail); + if (currentTail - currentHead <= 0 || (frozen && (currentTail - FreezeOffset - currentHead <= 0))) + { + result = default(T); + return false; + } + + // It's possible it could have become frozen after we checked _frozenForEnqueues + // and before reading the tail. That's ok: in that rare race condition, we just + // loop around again. + } + + // Lost a race. Spin a bit, then try again. + spinner.SpinOnce(); + } + } + + /// <summary> + /// Attempts to enqueue the item. If successful, the item will be stored + /// in the queue and true will be returned; otherwise, the item won't be stored, and false + /// will be returned. + /// </summary> + public bool TryEnqueue(T item) + { + // Loop in case of contention... + var spinner = new SpinWait(); + while (true) + { + // Get the tail at which to try to return. + int currentTail = Volatile.Read(ref _headAndTail.Tail); + int slotsIndex = currentTail & _slotsMask; + + // Read the sequence number for the tail position. + int sequenceNumber = Volatile.Read(ref _slots[slotsIndex].SequenceNumber); + + // The slot is empty and ready for us to enqueue into it if its sequence + // number matches the slot. + int diff = sequenceNumber - currentTail; + if (diff == 0) + { + // We may be racing with other enqueuers. Try to reserve the slot by incrementing + // the tail. Once we've done that, no one else will be able to write to this slot, + // and no dequeuer will be able to read from this slot until we've written the new + // sequence number. WARNING: The next few lines are not reliable on a runtime that + // supports thread aborts. If a thread abort were to sneak in after the CompareExchange + // but before the Volatile.Write, other threads will spin trying to access this slot. + // If this implementation is ever used on such a platform, this if block should be + // wrapped in a finally / prepared region. + if (Interlocked.CompareExchange(ref _headAndTail.Tail, currentTail + 1, currentTail) == currentTail) + { + // Successfully reserved the slot. Note that after the above CompareExchange, other threads + // trying to return will end up spinning until we do the subsequent Write. + _slots[slotsIndex].Item = item; + Volatile.Write(ref _slots[slotsIndex].SequenceNumber, currentTail + 1); + return true; + } + } + else if (diff < 0) + { + // The sequence number was less than what we needed, which means this slot still + // contains a value, i.e. the segment is full. Technically it's possible that multiple + // dequeuers could have read concurrently, with those getting later slots actually + // finishing first, so there could be spaces after this one that are available, but + // we need to enqueue in order. + return false; + } + + // Lost a race. Spin a bit, then try again. + spinner.SpinOnce(); + } + } + + /// <summary>Represents a slot in the queue.</summary> + [StructLayout(LayoutKind.Auto)] + [DebuggerDisplay("Item = {Item}, SequenceNumber = {SequenceNumber}")] + internal struct Slot + { + /// <summary>The item.</summary> + public T Item; + /// <summary>The sequence number for this slot, used to synchronize between enqueuers and dequeuers.</summary> + public int SequenceNumber; + } + } + } + + /// <summary>Padded head and tail indices, to avoid false sharing between producers and consumers.</summary> + [DebuggerDisplay("Head = {Head}, Tail = {Tail}")] + [StructLayout(LayoutKind.Explicit, Size = 384)] // padding before/between/after fields based on worst case cache line size of 128 + internal struct PaddedHeadAndTail + { + [FieldOffset(128)] public int Head; + [FieldOffset(256)] public int Tail; + } +} diff --git a/src/Common/src/System/Net/SecurityProtocol.cs b/src/Common/src/System/Net/SecurityProtocol.cs index beeba086ea..7d9565d92c 100644 --- a/src/Common/src/System/Net/SecurityProtocol.cs +++ b/src/Common/src/System/Net/SecurityProtocol.cs @@ -8,21 +8,8 @@ namespace System.Net { internal static class SecurityProtocol { - // SSLv2 and SSLv3 are considered insecure and will not be supported by the underlying implementations. - public const SslProtocols AllowedSecurityProtocols = - SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12; - - public const SslProtocols DefaultSecurityProtocols = - SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12; + public const SslProtocols DefaultSecurityProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12; public const SslProtocols SystemDefaultSecurityProtocols = SslProtocols.None; - - public static void ThrowOnNotAllowed(SslProtocols protocols, bool allowNone = true) - { - if ((!allowNone && (protocols == SslProtocols.None)) || ((protocols & ~AllowedSecurityProtocols) != 0)) - { - throw new NotSupportedException(SR.net_securityprotocolnotsupported); - } - } } } diff --git a/src/Common/src/System/Net/WebSockets/ManagedWebSocket.cs b/src/Common/src/System/Net/WebSockets/ManagedWebSocket.cs index 69cbd29dac..492c8cb63f 100644 --- a/src/Common/src/System/Net/WebSockets/ManagedWebSocket.cs +++ b/src/Common/src/System/Net/WebSockets/ManagedWebSocket.cs @@ -31,13 +31,11 @@ namespace System.Net.WebSockets /// <param name="isServer">true if this is the server-side of the connection; false if this is the client-side of the connection.</param> /// <param name="subprotocol">The agreed upon subprotocol for the connection.</param> /// <param name="keepAliveInterval">The interval to use for keep-alive pings.</param> - /// <param name="receiveBufferSize">The buffer size to use for received data.</param> - /// <param name="receiveBuffer">Optional buffer to use for receives.</param> /// <returns>The created <see cref="ManagedWebSocket"/> instance.</returns> public static ManagedWebSocket CreateFromConnectedStream( - Stream stream, bool isServer, string subprotocol, TimeSpan keepAliveInterval, Memory<byte> buffer) + Stream stream, bool isServer, string subprotocol, TimeSpan keepAliveInterval) { - return new ManagedWebSocket(stream, isServer, subprotocol, keepAliveInterval, buffer); + return new ManagedWebSocket(stream, isServer, subprotocol, keepAliveInterval); } /// <summary>Thread-safe random number generator used to generate masks for each send.</summary> @@ -159,9 +157,7 @@ namespace System.Net.WebSockets /// <param name="isServer">true if this is the server-side of the connection; false if this is the client-side of the connection.</param> /// <param name="subprotocol">The agreed upon subprotocol for the connection.</param> /// <param name="keepAliveInterval">The interval to use for keep-alive pings.</param> - /// <param name="receiveBufferSize">The buffer size to use for received data.</param> - /// <param name="buffer">Optional buffer to use for receives</param> - private ManagedWebSocket(Stream stream, bool isServer, string subprotocol, TimeSpan keepAliveInterval, Memory<byte> buffer) + private ManagedWebSocket(Stream stream, bool isServer, string subprotocol, TimeSpan keepAliveInterval) { Debug.Assert(StateUpdateLock != null, $"Expected {nameof(StateUpdateLock)} to be non-null"); Debug.Assert(ReceiveAsyncLock != null, $"Expected {nameof(ReceiveAsyncLock)} to be non-null"); @@ -176,17 +172,11 @@ namespace System.Net.WebSockets _isServer = isServer; _subprotocol = subprotocol; - // If we were provided with a buffer to use, use it as long as it's big enough for our needs. - // If it doesn't meet our criteria, just create a new one. If we need to create a new one, - // we avoid using the pool because often many web sockets will be used concurrently, and if each web - // socket rents a similarly sized buffer from the pool for its duration, we'll end up draining - // the pool, such that other web sockets will allocate anyway, as will anyone else in the process using the - // pool. If someone wants to pool, they can do so by passing in the buffer they want to use, and they can - // get it from whatever pool they like. If we create our own buffer, it's small, large enough for message - // headers and control payloads, but data for other message payloads is read directly into the buffers - // passed into ReceiveAsync. + // Create a buffer just large enough to handle received packet headers (at most 14 bytes) and + // control payloads (at most 125 bytes). Message payloads are read directly into the buffer + // supplied to ReceiveAsync. const int ReceiveBufferMinLength = MaxControlPayloadLength; - _receiveBuffer = buffer.Length >= ReceiveBufferMinLength ? buffer : new byte[ReceiveBufferMinLength]; + _receiveBuffer = new byte[ReceiveBufferMinLength]; // Set up the abort source so that if it's triggered, we transition the instance appropriately. _abortSource.Token.Register(s => diff --git a/src/Common/tests/System/Buffers/NativeOwnedMemory.cs b/src/Common/tests/System/Buffers/NativeMemoryManager.cs index 9e2c501cbf..e198eec977 100644 --- a/src/Common/tests/System/Buffers/NativeOwnedMemory.cs +++ b/src/Common/tests/System/Buffers/NativeMemoryManager.cs @@ -8,26 +8,26 @@ using System.Runtime.InteropServices; namespace System.Buffers { - internal sealed class NativeOwnedMemory : OwnedMemory<byte> + internal sealed class NativeMemoryManager : MemoryManager<byte> { private readonly int _length; private IntPtr _ptr; private int _retainedCount; private bool _disposed; - public NativeOwnedMemory(int length) + public NativeMemoryManager(int length) { _length = length; _ptr = Marshal.AllocHGlobal(length); } - ~NativeOwnedMemory() + ~NativeMemoryManager() { - Debug.WriteLine($"{nameof(NativeOwnedMemory)} being finalized"); + Debug.WriteLine($"{nameof(NativeMemoryManager)} being finalized"); Dispose(false); } - public override bool IsDisposed + public bool IsDisposed { get { @@ -40,7 +40,7 @@ namespace System.Buffers public override int Length => _length; - protected override bool IsRetained + public bool IsRetained { get { @@ -51,16 +51,26 @@ namespace System.Buffers } } - public override unsafe Span<byte> Span => new Span<byte>((void*)_ptr, _length); + public override unsafe Span<byte> GetSpan() => new Span<byte>((void*)_ptr, _length); - public override unsafe MemoryHandle Pin(int byteOffset = 0) + public override unsafe MemoryHandle Pin(int elementIndex = 0) { - if (byteOffset < 0 || byteOffset > _length) throw new ArgumentOutOfRangeException(nameof(byteOffset)); - void* pointer = (void*)((byte*)_ptr + byteOffset); - return new MemoryHandle(this, pointer); + if (elementIndex < 0 || elementIndex > _length) throw new ArgumentOutOfRangeException(nameof(elementIndex)); + + lock (this) + { + if (_retainedCount == 0 && _disposed) + { + throw new Exception(); + } + _retainedCount++; + } + + void* pointer = (void*)((byte*)_ptr + elementIndex); // T = byte + return new MemoryHandle(pointer, default, this); } - public override bool Release() + public override void Unpin() { lock (this) { @@ -74,23 +84,9 @@ namespace System.Buffers Marshal.FreeHGlobal(_ptr); _ptr = IntPtr.Zero; } - return true; } } } - return false; - } - - public override void Retain() - { - lock (this) - { - if (_retainedCount == 0 && _disposed) - { - throw new Exception(); - } - _retainedCount++; - } } protected override void Dispose(bool disposing) @@ -105,11 +101,5 @@ namespace System.Buffers } } } - - protected override bool TryGetArray(out ArraySegment<byte> segment) - { - segment = default(ArraySegment<byte>); - return false; - } } } diff --git a/src/Common/tests/System/Net/SslProtocolSupport.cs b/src/Common/tests/System/Net/SslProtocolSupport.cs index fb3b51028b..180ae00c28 100644 --- a/src/Common/tests/System/Net/SslProtocolSupport.cs +++ b/src/Common/tests/System/Net/SslProtocolSupport.cs @@ -4,6 +4,7 @@ using System.Collections; using System.Collections.Generic; +using System.Runtime.InteropServices; using System.Security.Authentication; namespace System.Net.Test.Common @@ -12,53 +13,37 @@ namespace System.Net.Test.Common { public const SslProtocols DefaultSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls; - public const SslProtocols SupportedSslProtocols = - SslProtocols.Tls - | SslProtocols.Tls11 - | SslProtocols.Tls12; - -#pragma warning disable 0618 - public const SslProtocols UnsupportedSslProtocols = - SslProtocols.Ssl2 - | SslProtocols.Ssl3; -#pragma warning restore 0618 - - public class SupportedSslProtocolsTestData : IEnumerable<object[]> + public static SslProtocols SupportedSslProtocols { - public IEnumerator<object[]> GetEnumerator() + get { - foreach (SslProtocols protocol in Enum.GetValues(typeof(SslProtocols))) + SslProtocols supported = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12; +#pragma warning disable 0618 // SSL2/3 are deprecated + if (PlatformDetection.IsWindows || + PlatformDetection.IsOSX || + (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && PlatformDetection.OpenSslVersion < new Version(1, 0, 2) && !PlatformDetection.IsDebian)) { - if (protocol != 0 && (protocol & SupportedSslProtocols) == protocol) - { - yield return new object[] { protocol }; - } + supported |= SslProtocols.Ssl3; } - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); +#pragma warning restore 0618 + return supported; } } - public class UnsupportedSslProtocolsTestData : IEnumerable<object[]> + public class SupportedSslProtocolsTestData : IEnumerable<object[]> { public IEnumerator<object[]> GetEnumerator() { foreach (SslProtocols protocol in Enum.GetValues(typeof(SslProtocols))) { - if (protocol != 0 && (protocol & UnsupportedSslProtocols) == protocol) + if (protocol != 0 && (protocol & SupportedSslProtocols) == protocol) { yield return new object[] { protocol }; } } } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } } diff --git a/src/Common/tests/System/Net/VirtualNetwork/VirtualNetwork.cs b/src/Common/tests/System/Net/VirtualNetwork/VirtualNetwork.cs index b82c3946d9..e34ffd7f4b 100644 --- a/src/Common/tests/System/Net/VirtualNetwork/VirtualNetwork.cs +++ b/src/Common/tests/System/Net/VirtualNetwork/VirtualNetwork.cs @@ -10,16 +10,29 @@ namespace System.Net.Test.Common { public class VirtualNetwork { + public class VirtualNetworkConnectionBroken : Exception + { + public VirtualNetworkConnectionBroken() : base("Connection broken") { } + } + private readonly int WaitForReadDataTimeoutMilliseconds = 30 * 1000; - + private readonly ConcurrentQueue<byte[]> _clientWriteQueue = new ConcurrentQueue<byte[]>(); private readonly ConcurrentQueue<byte[]> _serverWriteQueue = new ConcurrentQueue<byte[]>(); private readonly SemaphoreSlim _clientDataAvailable = new SemaphoreSlim(0); private readonly SemaphoreSlim _serverDataAvailable = new SemaphoreSlim(0); + public bool DisableConnectionBreaking { get; set; } = false; + private bool _connectionBroken = false; + public void ReadFrame(bool server, out byte[] buffer) { + if (_connectionBroken) + { + throw new VirtualNetworkConnectionBroken(); + } + SemaphoreSlim semaphore; ConcurrentQueue<byte[]> packetQueue; @@ -39,6 +52,11 @@ namespace System.Net.Test.Common throw new TimeoutException("VirtualNetwork: Timeout reading the next frame."); } + if (_connectionBroken) + { + throw new VirtualNetworkConnectionBroken(); + } + bool dequeueSucceeded = false; int remainingTries = 3; int backOffDelayMilliseconds = 2; @@ -62,6 +80,11 @@ namespace System.Net.Test.Common public void WriteFrame(bool server, byte[] buffer) { + if (_connectionBroken) + { + throw new VirtualNetworkConnectionBroken(); + } + SemaphoreSlim semaphore; ConcurrentQueue<byte[]> packetQueue; @@ -82,5 +105,15 @@ namespace System.Net.Test.Common packetQueue.Enqueue(innerBuffer); semaphore.Release(); } + + public void BreakConnection() + { + if (!DisableConnectionBreaking) + { + _connectionBroken = true; + _serverDataAvailable.Release(1_000_000); + _clientDataAvailable.Release(1_000_000); + } + } } } diff --git a/src/Common/tests/System/Net/VirtualNetwork/VirtualNetworkStream.cs b/src/Common/tests/System/Net/VirtualNetwork/VirtualNetworkStream.cs index e04eb54ace..d019c101ad 100644 --- a/src/Common/tests/System/Net/VirtualNetwork/VirtualNetworkStream.cs +++ b/src/Common/tests/System/Net/VirtualNetwork/VirtualNetworkStream.cs @@ -156,5 +156,15 @@ namespace System.Net.Test.Common public override void EndWrite(IAsyncResult asyncResult) => TaskToApm.End(asyncResult); + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _network.BreakConnection(); + } + + base.Dispose(disposing); + } } } diff --git a/src/CoreFx.Private.TestUtilities/src/System/Diagnostics/RemoteExecutorTestBase.cs b/src/CoreFx.Private.TestUtilities/src/System/Diagnostics/RemoteExecutorTestBase.cs index ac039bd592..bf8331cd6f 100644 --- a/src/CoreFx.Private.TestUtilities/src/System/Diagnostics/RemoteExecutorTestBase.cs +++ b/src/CoreFx.Private.TestUtilities/src/System/Diagnostics/RemoteExecutorTestBase.cs @@ -20,7 +20,7 @@ namespace System.Diagnostics public const int SuccessExitCode = 42; /// <summary>The name of the test console app.</summary> - protected static readonly string TestConsoleApp = "RemoteExecutorConsoleApp.exe"; + protected static readonly string TestConsoleApp = Path.GetFullPath("RemoteExecutorConsoleApp.exe"); /// <summary>Invokes the method from this assembly in another process using the specified arguments.</summary> /// <param name="method">The method to invoke.</param> diff --git a/src/Microsoft.VisualBasic/src/MatchingRefApiCompatBaseline.netstandard.txt b/src/Microsoft.VisualBasic/src/MatchingRefApiCompatBaseline.netstandard.txt index 11a2bf0209..33bf1c3233 100644 --- a/src/Microsoft.VisualBasic/src/MatchingRefApiCompatBaseline.netstandard.txt +++ b/src/Microsoft.VisualBasic/src/MatchingRefApiCompatBaseline.netstandard.txt @@ -1,16 +1,7 @@ Compat issues with assembly Microsoft.VisualBasic: -TypesMustExist : Type 'Microsoft.VisualBasic.ControlChars' does not exist in the implementation but it does exist in the contract. -CannotSealType : Type 'Microsoft.VisualBasic.HideModuleNameAttribute' is sealed in the implementation but not sealed in the contract. -TypesMustExist : Type 'Microsoft.VisualBasic.Interaction' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'Microsoft.VisualBasic.Strings.Left(System.String, System.Int32)' does not exist in the implementation but it does exist in the contract. + +# Loaded via reflection so needs to be public in the implementation but we don't want to expose them publicly in the ref assembly: MembersMustExist : Member 'Microsoft.VisualBasic.CompilerServices.Conversions.FallbackUserDefinedConversion(System.Object, System.Type)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'Microsoft.VisualBasic.CompilerServices.Conversions.GetCultureInfo()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'Microsoft.VisualBasic.CompilerServices.Conversions.IsHexOrOctValue(System.String, System.Int64)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'Microsoft.VisualBasic.CompilerServices.Conversions.IsHexOrOctValue(System.String, System.UInt64)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'Microsoft.VisualBasic.CompilerServices.Conversions.ToHalfwidthNumbers(System.String, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract. -CannotSealType : Type 'Microsoft.VisualBasic.CompilerServices.DesignerGeneratedAttribute' is sealed in the implementation but not sealed in the contract. -CannotSealType : Type 'Microsoft.VisualBasic.CompilerServices.IncompleteInitialization' is sealed in the implementation but not sealed in the contract. -TypesMustExist : Type 'Microsoft.VisualBasic.CompilerServices.InternalErrorException' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'Microsoft.VisualBasic.CompilerServices.NewLateBinding.FallbackCall(System.Object, System.String, System.Object[], System.String[], System.Boolean)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'Microsoft.VisualBasic.CompilerServices.NewLateBinding.FallbackGet(System.Object, System.String, System.Object[], System.String[])' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'Microsoft.VisualBasic.CompilerServices.NewLateBinding.FallbackIndexSet(System.Object, System.Object[], System.String[])' does not exist in the implementation but it does exist in the contract. @@ -20,12 +11,8 @@ MembersMustExist : Member 'Microsoft.VisualBasic.CompilerServices.NewLateBinding MembersMustExist : Member 'Microsoft.VisualBasic.CompilerServices.NewLateBinding.FallbackSet(System.Object, System.String, System.Object[])' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'Microsoft.VisualBasic.CompilerServices.NewLateBinding.FallbackSetComplex(System.Object, System.String, System.Object[], System.Boolean, System.Boolean)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'Microsoft.VisualBasic.CompilerServices.NewLateBinding.LateCallInvokeDefault(System.Object, System.Object[], System.String[], System.Boolean)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'Microsoft.VisualBasic.CompilerServices.NewLateBinding.LateCanEvaluate(System.Object, System.Type, System.String, System.Object[], System.Boolean, System.Boolean)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'Microsoft.VisualBasic.CompilerServices.NewLateBinding.LateGetInvokeDefault(System.Object, System.Object[], System.String[], System.Boolean)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'Microsoft.VisualBasic.CompilerServices.Operators.CompareObject(System.Object, System.Object, System.Boolean)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'Microsoft.VisualBasic.CompilerServices.Operators.FallbackInvokeUserDefinedOperator(System.Object, System.Object[])' does not exist in the implementation but it does exist in the contract. -CannotSealType : Type 'Microsoft.VisualBasic.CompilerServices.OptionCompareAttribute' is sealed in the implementation but not sealed in the contract. -CannotSealType : Type 'Microsoft.VisualBasic.CompilerServices.OptionTextAttribute' is sealed in the implementation but not sealed in the contract. TypesMustExist : Type 'Microsoft.VisualBasic.CompilerServices.SiteDelegate0' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Microsoft.VisualBasic.CompilerServices.SiteDelegate1' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Microsoft.VisualBasic.CompilerServices.SiteDelegate2' does not exist in the implementation but it does exist in the contract. @@ -34,8 +21,3 @@ TypesMustExist : Type 'Microsoft.VisualBasic.CompilerServices.SiteDelegate4' doe TypesMustExist : Type 'Microsoft.VisualBasic.CompilerServices.SiteDelegate5' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Microsoft.VisualBasic.CompilerServices.SiteDelegate6' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Microsoft.VisualBasic.CompilerServices.SiteDelegate7' does not exist in the implementation but it does exist in the contract. -CannotSealType : Type 'Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute' is sealed in the implementation but not sealed in the contract. -CannotSealType : Type 'Microsoft.VisualBasic.CompilerServices.StaticLocalInitFlag' is sealed in the implementation but not sealed in the contract. -MembersMustExist : Member 'Microsoft.VisualBasic.CompilerServices.Utils.GetResourceString(System.String, System.String[])' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'Microsoft.VisualBasic.CompilerServices.Utils.MethodToString(System.Reflection.MethodBase)' does not exist in the implementation but it does exist in the contract. -Total Issues: 39 diff --git a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/Conversions.vb b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/Conversions.vb index 462ab72f67..8aba342f87 100644 --- a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/Conversions.vb +++ b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/Conversions.vb @@ -919,13 +919,13 @@ MisMatch: Public Shared Shadows Function ToString(value As Char) As String Return value.ToString() End Function - Public Shared Function GetCultureInfo() As Global.System.Globalization.CultureInfo + Friend Shared Function GetCultureInfo() As Global.System.Globalization.CultureInfo Return Global.System.Globalization.CultureInfo.CurrentCulture End Function - Public Shared Function ToHalfwidthNumbers(s As String, culture As Global.System.Globalization.CultureInfo) As String + Friend Shared Function ToHalfwidthNumbers(s As String, culture As Global.System.Globalization.CultureInfo) As String Return s End Function - Public Shared Function IsHexOrOctValue(value As String, ByRef i64Value As Global.System.Int64) As Boolean + Friend Shared Function IsHexOrOctValue(value As String, ByRef i64Value As Global.System.Int64) As Boolean Dim ch As Char Dim length As Integer Dim firstNonspace As Integer @@ -955,7 +955,7 @@ GetSpecialValue: Return True End Function <Global.System.CLSCompliant(False)> - Public Shared Function IsHexOrOctValue(value As String, ByRef ui64Value As Global.System.UInt64) As Boolean + Friend Shared Function IsHexOrOctValue(value As String, ByRef ui64Value As Global.System.UInt64) As Boolean Dim ch As Char Dim length As Integer Dim firstNonspace As Integer diff --git a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/DesignerGeneratedAttribute.vb b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/DesignerGeneratedAttribute.vb index d49ffce83e..26b43aac1c 100644 --- a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/DesignerGeneratedAttribute.vb +++ b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/DesignerGeneratedAttribute.vb @@ -5,7 +5,7 @@ Namespace Global.Microsoft.VisualBasic.CompilerServices <Global.System.AttributeUsage(Global.System.AttributeTargets.Class, Inherited:=False)> <Global.System.ComponentModel.EditorBrowsable(Global.System.ComponentModel.EditorBrowsableState.Never)> - Public Class DesignerGeneratedAttribute + Public NotInheritable Class DesignerGeneratedAttribute Inherits Global.System.Attribute End Class End Namespace diff --git a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/ExceptionUtils.vb b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/ExceptionUtils.vb index 9e1a658cc5..eb5f7e7f3c 100644 --- a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/ExceptionUtils.vb +++ b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/ExceptionUtils.vb @@ -58,7 +58,7 @@ Namespace Microsoft.VisualBasic.CompilerServices End Class - Public NotInheritable Class InternalErrorException + Friend NotInheritable Class InternalErrorException Inherits System.Exception Public Sub New(ByVal message As String) diff --git a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/IncompleteInitialization.vb b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/IncompleteInitialization.vb index 52f2af042c..cbb88b3a6b 100644 --- a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/IncompleteInitialization.vb +++ b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/IncompleteInitialization.vb @@ -5,7 +5,7 @@ Namespace Global.Microsoft.VisualBasic.CompilerServices <Global.System.Diagnostics.DebuggerNonUserCode()> <Global.System.ComponentModel.EditorBrowsable(Global.System.ComponentModel.EditorBrowsableState.Never)> - Public Class IncompleteInitialization + Public NotInheritable Class IncompleteInitialization Inherits Global.System.Exception Public Sub New() MyBase.New() diff --git a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/NewLateBinding.vb b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/NewLateBinding.vb index effcd30367..20ae0ccb1a 100644 --- a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/NewLateBinding.vb +++ b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/NewLateBinding.vb @@ -30,7 +30,7 @@ Namespace Microsoft.VisualBasic.CompilerServices End Sub <DebuggerHiddenAttribute()> <DebuggerStepThroughAttribute()> - Public Shared Function LateCanEvaluate( + Friend Shared Function LateCanEvaluate( ByVal instance As Object, ByVal type As System.Type, ByVal memberName As String, diff --git a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/Operators.vb b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/Operators.vb index 1d68abcb79..d26866c792 100644 --- a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/Operators.vb +++ b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/Operators.vb @@ -350,7 +350,7 @@ Namespace Microsoft.VisualBasic.CompilerServices End Select End Function - Public Shared Function CompareObject(ByVal left As Object, ByVal right As Object, ByVal textCompare As Boolean) As Integer + Friend Shared Function CompareObject(ByVal left As Object, ByVal right As Object, ByVal textCompare As Boolean) As Integer Dim comparison As CompareClass = CompareObject2(left, right, textCompare) Select Case comparison diff --git a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/OptionCompareAttribute.vb b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/OptionCompareAttribute.vb index b3b601a0cb..ebe8a31dec 100644 --- a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/OptionCompareAttribute.vb +++ b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/OptionCompareAttribute.vb @@ -5,7 +5,7 @@ Namespace Global.Microsoft.VisualBasic.CompilerServices <Global.System.AttributeUsage(Global.System.AttributeTargets.Parameter, Inherited:=False)> <Global.System.ComponentModel.EditorBrowsable(Global.System.ComponentModel.EditorBrowsableState.Never)> - Public Class OptionCompareAttribute + Public NotInheritable Class OptionCompareAttribute Inherits Global.System.Attribute End Class End Namespace diff --git a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/OptionTextAttribute.vb b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/OptionTextAttribute.vb index 2b275ddc9a..e564f11314 100644 --- a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/OptionTextAttribute.vb +++ b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/OptionTextAttribute.vb @@ -5,7 +5,7 @@ Namespace Global.Microsoft.VisualBasic.CompilerServices <Global.System.AttributeUsage(Global.System.AttributeTargets.Class, Inherited:=False)> <Global.System.ComponentModel.EditorBrowsable(Global.System.ComponentModel.EditorBrowsableState.Never)> - Public Class OptionTextAttribute + Public NotInheritable Class OptionTextAttribute Inherits Global.System.Attribute End Class End Namespace diff --git a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/StandardModuleAttribute.vb b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/StandardModuleAttribute.vb index 0e2fc0e45a..f333352b4f 100644 --- a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/StandardModuleAttribute.vb +++ b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/StandardModuleAttribute.vb @@ -5,7 +5,7 @@ Namespace Global.Microsoft.VisualBasic.CompilerServices <Global.System.AttributeUsage(Global.System.AttributeTargets.Class, Inherited:=False)> <Global.System.ComponentModel.EditorBrowsable(Global.System.ComponentModel.EditorBrowsableState.Never)> - Public Class StandardModuleAttribute + Public NotInheritable Class StandardModuleAttribute Inherits Global.System.Attribute End Class End Namespace diff --git a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/StaticLocalInitFlag.vb b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/StaticLocalInitFlag.vb index 4805db4aa2..e98b8cf3c2 100644 --- a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/StaticLocalInitFlag.vb +++ b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/StaticLocalInitFlag.vb @@ -5,7 +5,7 @@ Namespace Global.Microsoft.VisualBasic.CompilerServices <Global.System.Diagnostics.DebuggerNonUserCode()> <Global.System.ComponentModel.EditorBrowsable(Global.System.ComponentModel.EditorBrowsableState.Never)> - Public Class StaticLocalInitFlag + Public NotInheritable Class StaticLocalInitFlag Public State As Short End Class End Namespace diff --git a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/Utils.LateBinder.vb b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/Utils.LateBinder.vb index 00dc926510..adab60aebf 100644 --- a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/Utils.LateBinder.vb +++ b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/CompilerServices/Utils.LateBinder.vb @@ -79,7 +79,7 @@ Namespace Microsoft.VisualBasic.CompilerServices ' Param: Args - An array of params used to replace placeholders. 'Returns: The resource string if found or an error message string '***************************************************************************** - Public Shared Function GetResourceString(ByVal resourceKey As String, ByVal ParamArray args() As String) As String + Friend Shared Function GetResourceString(ByVal resourceKey As String, ByVal ParamArray args() As String) As String Return SR.Format(resourceKey, args) End Function @@ -388,7 +388,7 @@ GetSpecialValue: Return resultString End Function - Public Shared Function MethodToString(ByVal method As Reflection.MethodBase) As String + Friend Shared Function MethodToString(ByVal method As Reflection.MethodBase) As String Dim returnType As System.Type = Nothing Dim first As Boolean diff --git a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/ControlChars.vb b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/ControlChars.vb index 205461bb4b..88bfd165f4 100644 --- a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/ControlChars.vb +++ b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/ControlChars.vb @@ -4,7 +4,7 @@ Namespace Microsoft.VisualBasic ' Contants for the Control Characters - Public NotInheritable Class ControlChars + Friend NotInheritable Class ControlChars Public Const CrLf As String = ChrW(13) & ChrW(10) Public Const NewLine As String = ChrW(13) & ChrW(10) diff --git a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/HideModuleNameAttribute.vb b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/HideModuleNameAttribute.vb index 3cc0fe8fb6..55d700ea05 100644 --- a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/HideModuleNameAttribute.vb +++ b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/HideModuleNameAttribute.vb @@ -5,7 +5,7 @@ Namespace Global.Microsoft.VisualBasic <Global.System.AttributeUsage(Global.System.AttributeTargets.Class, Inherited:=False)> <Global.System.ComponentModel.EditorBrowsable(Global.System.ComponentModel.EditorBrowsableState.Never)> - Public Class HideModuleNameAttribute + Public NotInheritable Class HideModuleNameAttribute Inherits Global.System.Attribute End Class End Namespace diff --git a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/Interaction.vb b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/Interaction.vb index 2dca6e194a..e68edca47c 100644 --- a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/Interaction.vb +++ b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/Interaction.vb @@ -4,7 +4,7 @@ Namespace Microsoft.VisualBasic - Public Module Interaction + Friend Module Interaction Friend Function IIf(Of T)(ByVal condition As Boolean, ByVal truePart As T, ByVal falsePart As T) As T If condition Then diff --git a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/Strings.vb b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/Strings.vb index eb660c2507..cc94d835d7 100644 --- a/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/Strings.vb +++ b/src/Microsoft.VisualBasic/src/Microsoft/VisualBasic/Strings.vb @@ -28,7 +28,7 @@ Namespace Global.Microsoft.VisualBasic '============================================================================ ' Left/Right/Mid/Trim functions. '============================================================================ - Public Function Left(ByVal [str] As String, ByVal length As Integer) As String + Friend Function Left(ByVal [str] As String, ByVal length As Integer) As String '------------------------------------------------------------- ' lLen < 0 throws InvalidArgument exception ' lLen > Len([str]) let lLen = Len([str]) diff --git a/src/Native/Unix/System.Net.Http.Native/pal_easy.cpp b/src/Native/Unix/System.Net.Http.Native/pal_easy.cpp index 4ccbd8f661..f68c793e02 100644 --- a/src/Native/Unix/System.Net.Http.Native/pal_easy.cpp +++ b/src/Native/Unix/System.Net.Http.Native/pal_easy.cpp @@ -74,6 +74,8 @@ static_assert(PAL_CURL_HTTP_VERSION_1_1 == CURL_HTTP_VERSION_1_1, ""); static_assert(PAL_CURL_HTTP_VERSION_2TLS == CURL_HTTP_VERSION_2TLS, ""); #endif +static_assert(PAL_CURL_SSLVERSION_SSLv2 == CURL_SSLVERSION_SSLv2, ""); +static_assert(PAL_CURL_SSLVERSION_SSLv3 == CURL_SSLVERSION_SSLv3, ""); static_assert(PAL_CURL_SSLVERSION_TLSv1 == CURL_SSLVERSION_TLSv1, ""); #if HAVE_CURL_SSLVERSION_TLSv1_012 static_assert(PAL_CURL_SSLVERSION_TLSv1_0 == CURL_SSLVERSION_TLSv1_0, ""); diff --git a/src/Native/Unix/System.Net.Http.Native/pal_easy.h b/src/Native/Unix/System.Net.Http.Native/pal_easy.h index 18298c1532..62f49c2d33 100644 --- a/src/Native/Unix/System.Net.Http.Native/pal_easy.h +++ b/src/Native/Unix/System.Net.Http.Native/pal_easy.h @@ -100,6 +100,8 @@ enum PAL_CURL_HTTP_VERSION enum PAL_CURL_SSLVERSION { PAL_CURL_SSLVERSION_TLSv1 = 1, + PAL_CURL_SSLVERSION_SSLv2 = 2, + PAL_CURL_SSLVERSION_SSLv3 = 3, PAL_CURL_SSLVERSION_TLSv1_0 = 4, PAL_CURL_SSLVERSION_TLSv1_1 = 5, PAL_CURL_SSLVERSION_TLSv1_2 = 6, diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509chain.cpp b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509chain.cpp index 6db3d53e9f..4bed8b8940 100644 --- a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509chain.cpp +++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509chain.cpp @@ -154,7 +154,8 @@ static void MergeStatusCodes(CFTypeRef key, CFTypeRef value, void* context) CFStringRef keyString = reinterpret_cast<CFStringRef>(key); if (CFEqual(keyString, CFSTR("NotValidBefore")) || CFEqual(keyString, CFSTR("ValidLeaf")) || - CFEqual(keyString, CFSTR("ValidIntermediates")) || CFEqual(keyString, CFSTR("ValidRoot"))) + CFEqual(keyString, CFSTR("ValidIntermediates")) || CFEqual(keyString, CFSTR("ValidRoot")) || + CFEqual(keyString, CFSTR("TemporalValidity"))) *pStatus |= PAL_X509ChainNotTimeValid; else if (CFEqual(keyString, CFSTR("Revocation"))) *pStatus |= PAL_X509ChainRevoked; @@ -168,8 +169,10 @@ static void MergeStatusCodes(CFTypeRef key, CFTypeRef value, void* context) *pStatus |= PAL_X509ChainExplicitDistrust; else if (CFEqual(keyString, CFSTR("RevocationResponseRequired"))) *pStatus |= PAL_X509ChainRevocationStatusUnknown; + else if (CFEqual(keyString, CFSTR("MissingIntermediate"))) + *pStatus |= PAL_X509ChainPartialChain; else if (CFEqual(keyString, CFSTR("WeakLeaf")) || CFEqual(keyString, CFSTR("WeakIntermediates")) || - CFEqual(keyString, CFSTR("WeakRoot"))) + CFEqual(keyString, CFSTR("WeakRoot")) || CFEqual(keyString, CFSTR("WeakKeySize"))) { // Because we won't report this out of a chain built by .NET on Windows, // don't report it here. diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_ssl.cpp b/src/Native/Unix/System.Security.Cryptography.Native/pal_ssl.cpp index 7489aff1a0..5b6ce02160 100644 --- a/src/Native/Unix/System.Security.Cryptography.Native/pal_ssl.cpp +++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_ssl.cpp @@ -80,13 +80,8 @@ extern "C" void CryptoNative_SetProtocolOptions(SSL_CTX* ctx, SslProtocols proto { protocolOptions |= SSL_OP_NO_SSLv2; } -#ifndef OPENSSL_NO_SSL3 if ((protocols & PAL_SSL_SSL3) != PAL_SSL_SSL3) -#endif { - // If OPENSSL_NO_SSL3 is defined, then ensure we always include - // SSL_OP_NO_SSLv3 in case we end up running against a binary - // which had SSLv3 enabled (we don't want to use SSLv3 in that case). protocolOptions |= SSL_OP_NO_SSLv3; } if ((protocols & PAL_SSL_TLS) != PAL_SSL_TLS) diff --git a/src/System.Collections.Concurrent/src/System.Collections.Concurrent.csproj b/src/System.Collections.Concurrent/src/System.Collections.Concurrent.csproj index e8c7b2cd6a..4f7e2de607 100644 --- a/src/System.Collections.Concurrent/src/System.Collections.Concurrent.csproj +++ b/src/System.Collections.Concurrent/src/System.Collections.Concurrent.csproj @@ -22,6 +22,9 @@ <Compile Include="System\Collections\Concurrent\Partitioner.cs" /> <Compile Include="System\Collections\Concurrent\PartitionerStatic.cs" /> <Compile Include="System\Collections\Concurrent\PlatformHelper.cs" /> + <Compile Include="$(CommonPath)\System\Collections\Concurrent\ConcurrentQueue_Segment.cs"> + <Link>Common\System\Collections\Concurrent\ConcurrentQueue_Segment.cs</Link> + </Compile> </ItemGroup> <ItemGroup> <Reference Include="System.Collections" /> diff --git a/src/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentQueue.cs b/src/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentQueue.cs index c4202e1a2c..3f78e24527 100644 --- a/src/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentQueue.cs +++ b/src/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentQueue.cs @@ -21,7 +21,7 @@ namespace System.Collections.Concurrent [DebuggerDisplay("Count = {Count}")] [DebuggerTypeProxy(typeof(IProducerConsumerCollectionDebugView<>))] [Serializable] - public class ConcurrentQueue<T> : IProducerConsumerCollection<T>, IReadOnlyCollection<T> + public partial class ConcurrentQueue<T> : IProducerConsumerCollection<T>, IReadOnlyCollection<T> { // This implementation provides an unbounded, multi-producer multi-consumer queue // that supports the standard Enqueue/TryDequeue operations, as well as support for @@ -88,7 +88,7 @@ namespace System.Collections.Concurrent int count = c.Count; if (count > length) { - length = Math.Min(RoundUpToPowerOf2(count), MaxSegmentLength); + length = Math.Min(Segment.RoundUpToPowerOf2(count), MaxSegmentLength); } } @@ -589,18 +589,6 @@ namespace System.Collections.Concurrent } } - /// <summary>Round the specified value up to the next power of 2, if it isn't one already.</summary> - private static int RoundUpToPowerOf2(int i) - { - --i; - i |= i >> 1; - i |= i >> 2; - i |= i >> 4; - i |= i >> 8; - i |= i >> 16; - return i + 1; - } - /// <summary>Adds an object to the end of the <see cref="ConcurrentQueue{T}"/>.</summary> /// <param name="item"> /// The object to add to the end of the <see cref="ConcurrentQueue{T}"/>. @@ -812,312 +800,5 @@ namespace System.Collections.Concurrent _tail = _head = new Segment(InitialSegmentLength); } } - - /// <summary> - /// Provides a multi-producer, multi-consumer thread-safe bounded segment. When the queue is full, - /// enqueues fail and return false. When the queue is empty, dequeues fail and return null. - /// These segments are linked together to form the unbounded <see cref="ConcurrentQueue{T}"/>. - /// </summary> - [DebuggerDisplay("Capacity = {Capacity}")] - private sealed class Segment - { - // Segment design is inspired by the algorithm outlined at: - // http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue - - /// <summary>The array of items in this queue. Each slot contains the item in that slot and its "sequence number".</summary> - internal readonly Slot[] _slots; - /// <summary>Mask for quickly accessing a position within the queue's array.</summary> - internal readonly int _slotsMask; - /// <summary>The head and tail positions, with padding to help avoid false sharing contention.</summary> - /// <remarks>Dequeuing happens from the head, enqueuing happens at the tail.</remarks> - internal PaddedHeadAndTail _headAndTail; // mutable struct: do not make this readonly - - /// <summary>Indicates whether the segment has been marked such that dequeues don't overwrite the removed data.</summary> - internal bool _preservedForObservation; - /// <summary>Indicates whether the segment has been marked such that no additional items may be enqueued.</summary> - internal bool _frozenForEnqueues; - /// <summary>The segment following this one in the queue, or null if this segment is the last in the queue.</summary> - internal Segment _nextSegment; - - /// <summary>Creates the segment.</summary> - /// <param name="boundedLength"> - /// The maximum number of elements the segment can contain. Must be a power of 2. - /// </param> - public Segment(int boundedLength) - { - // Validate the length - Debug.Assert(boundedLength >= 2, $"Must be >= 2, got {boundedLength}"); - Debug.Assert((boundedLength & (boundedLength - 1)) == 0, $"Must be a power of 2, got {boundedLength}"); - - // Initialize the slots and the mask. The mask is used as a way of quickly doing "% _slots.Length", - // instead letting us do "& _slotsMask". - _slots = new Slot[boundedLength]; - _slotsMask = boundedLength - 1; - - // Initialize the sequence number for each slot. The sequence number provides a ticket that - // allows dequeuers to know whether they can dequeue and enqueuers to know whether they can - // enqueue. An enqueuer at position N can enqueue when the sequence number is N, and a dequeuer - // for position N can dequeue when the sequence number is N + 1. When an enqueuer is done writing - // at position N, it sets the sequence number to N + 1 so that a dequeuer will be able to dequeue, - // and when a dequeuer is done dequeueing at position N, it sets the sequence number to N + _slots.Length, - // so that when an enqueuer loops around the slots, it'll find that the sequence number at - // position N is N. This also means that when an enqueuer finds that at position N the sequence - // number is < N, there is still a value in that slot, i.e. the segment is full, and when a - // dequeuer finds that the value in a slot is < N + 1, there is nothing currently available to - // dequeue. (It is possible for multiple enqueuers to enqueue concurrently, writing into - // subsequent slots, and to have the first enqueuer take longer, so that the slots for 1, 2, 3, etc. - // may have values, but the 0th slot may still be being filled... in that case, TryDequeue will - // return false.) - for (int i = 0; i < _slots.Length; i++) - { - _slots[i].SequenceNumber = i; - } - } - - /// <summary>Gets the number of elements this segment can store.</summary> - internal int Capacity => _slots.Length; - - /// <summary>Gets the "freeze offset" for this segment.</summary> - internal int FreezeOffset => _slots.Length * 2; - - /// <summary> - /// Ensures that the segment will not accept any subsequent enqueues that aren't already underway. - /// </summary> - /// <remarks> - /// When we mark a segment as being frozen for additional enqueues, - /// we set the <see cref="_frozenForEnqueues"/> bool, but that's mostly - /// as a small helper to avoid marking it twice. The real marking comes - /// by modifying the Tail for the segment, increasing it by this - /// <see cref="FreezeOffset"/>. This effectively knocks it off the - /// sequence expected by future enqueuers, such that any additional enqueuer - /// will be unable to enqueue due to it not lining up with the expected - /// sequence numbers. This value is chosen specially so that Tail will grow - /// to a value that maps to the same slot but that won't be confused with - /// any other enqueue/dequeue sequence number. - /// </remarks> - internal void EnsureFrozenForEnqueues() // must only be called while queue's segment lock is held - { - if (!_frozenForEnqueues) // flag used to ensure we don't increase the Tail more than once if frozen more than once - { - _frozenForEnqueues = true; - - // Increase the tail by FreezeOffset, spinning until we're successful in doing so. - var spinner = new SpinWait(); - while (true) - { - int tail = Volatile.Read(ref _headAndTail.Tail); - if (Interlocked.CompareExchange(ref _headAndTail.Tail, tail + FreezeOffset, tail) == tail) - { - break; - } - spinner.SpinOnce(); - } - } - } - - /// <summary>Tries to dequeue an element from the queue.</summary> - public bool TryDequeue(out T item) - { - // Loop in case of contention... - var spinner = new SpinWait(); - while (true) - { - // Get the head at which to try to dequeue. - int currentHead = Volatile.Read(ref _headAndTail.Head); - int slotsIndex = currentHead & _slotsMask; - - // Read the sequence number for the head position. - int sequenceNumber = Volatile.Read(ref _slots[slotsIndex].SequenceNumber); - - // We can dequeue from this slot if it's been filled by an enqueuer, which - // would have left the sequence number at pos+1. - int diff = sequenceNumber - (currentHead + 1); - if (diff == 0) - { - // We may be racing with other dequeuers. Try to reserve the slot by incrementing - // the head. Once we've done that, no one else will be able to read from this slot, - // and no enqueuer will be able to read from this slot until we've written the new - // sequence number. WARNING: The next few lines are not reliable on a runtime that - // supports thread aborts. If a thread abort were to sneak in after the CompareExchange - // but before the Volatile.Write, enqueuers trying to enqueue into this slot would - // spin indefinitely. If this implementation is ever used on such a platform, this - // if block should be wrapped in a finally / prepared region. - if (Interlocked.CompareExchange(ref _headAndTail.Head, currentHead + 1, currentHead) == currentHead) - { - // Successfully reserved the slot. Note that after the above CompareExchange, other threads - // trying to dequeue from this slot will end up spinning until we do the subsequent Write. - item = _slots[slotsIndex].Item; - if (!Volatile.Read(ref _preservedForObservation)) - { - // If we're preserving, though, we don't zero out the slot, as we need it for - // enumerations, peeking, ToArray, etc. And we don't update the sequence number, - // so that an enqueuer will see it as full and be forced to move to a new segment. - _slots[slotsIndex].Item = default(T); - Volatile.Write(ref _slots[slotsIndex].SequenceNumber, currentHead + _slots.Length); - } - return true; - } - } - else if (diff < 0) - { - // The sequence number was less than what we needed, which means this slot doesn't - // yet contain a value we can dequeue, i.e. the segment is empty. Technically it's - // possible that multiple enqueuers could have written concurrently, with those - // getting later slots actually finishing first, so there could be elements after - // this one that are available, but we need to dequeue in order. So before declaring - // failure and that the segment is empty, we check the tail to see if we're actually - // empty or if we're just waiting for items in flight or after this one to become available. - bool frozen = _frozenForEnqueues; - int currentTail = Volatile.Read(ref _headAndTail.Tail); - if (currentTail - currentHead <= 0 || (frozen && (currentTail - FreezeOffset - currentHead <= 0))) - { - item = default(T); - return false; - } - - // It's possible it could have become frozen after we checked _frozenForEnqueues - // and before reading the tail. That's ok: in that rare race condition, we just - // loop around again. - } - - // Lost a race. Spin a bit, then try again. - spinner.SpinOnce(); - } - } - - /// <summary>Tries to peek at an element from the queue, without removing it.</summary> - public bool TryPeek(out T result, bool resultUsed) - { - if (resultUsed) - { - // In order to ensure we don't get a torn read on the value, we mark the segment - // as preserving for observation. Additional items can still be enqueued to this - // segment, but no space will be freed during dequeues, such that the segment will - // no longer be reusable. - _preservedForObservation = true; - Interlocked.MemoryBarrier(); - } - - // Loop in case of contention... - var spinner = new SpinWait(); - while (true) - { - // Get the head at which to try to peek. - int currentHead = Volatile.Read(ref _headAndTail.Head); - int slotsIndex = currentHead & _slotsMask; - - // Read the sequence number for the head position. - int sequenceNumber = Volatile.Read(ref _slots[slotsIndex].SequenceNumber); - - // We can peek from this slot if it's been filled by an enqueuer, which - // would have left the sequence number at pos+1. - int diff = sequenceNumber - (currentHead + 1); - if (diff == 0) - { - result = resultUsed ? _slots[slotsIndex].Item : default(T); - return true; - } - else if (diff < 0) - { - // The sequence number was less than what we needed, which means this slot doesn't - // yet contain a value we can peek, i.e. the segment is empty. Technically it's - // possible that multiple enqueuers could have written concurrently, with those - // getting later slots actually finishing first, so there could be elements after - // this one that are available, but we need to peek in order. So before declaring - // failure and that the segment is empty, we check the tail to see if we're actually - // empty or if we're just waiting for items in flight or after this one to become available. - bool frozen = _frozenForEnqueues; - int currentTail = Volatile.Read(ref _headAndTail.Tail); - if (currentTail - currentHead <= 0 || (frozen && (currentTail - FreezeOffset - currentHead <= 0))) - { - result = default(T); - return false; - } - - // It's possible it could have become frozen after we checked _frozenForEnqueues - // and before reading the tail. That's ok: in that rare race condition, we just - // loop around again. - } - - // Lost a race. Spin a bit, then try again. - spinner.SpinOnce(); - } - } - - /// <summary> - /// Attempts to enqueue the item. If successful, the item will be stored - /// in the queue and true will be returned; otherwise, the item won't be stored, and false - /// will be returned. - /// </summary> - public bool TryEnqueue(T item) - { - // Loop in case of contention... - var spinner = new SpinWait(); - while (true) - { - // Get the tail at which to try to return. - int currentTail = Volatile.Read(ref _headAndTail.Tail); - int slotsIndex = currentTail & _slotsMask; - - // Read the sequence number for the tail position. - int sequenceNumber = Volatile.Read(ref _slots[slotsIndex].SequenceNumber); - - // The slot is empty and ready for us to enqueue into it if its sequence - // number matches the slot. - int diff = sequenceNumber - currentTail; - if (diff == 0) - { - // We may be racing with other enqueuers. Try to reserve the slot by incrementing - // the tail. Once we've done that, no one else will be able to write to this slot, - // and no dequeuer will be able to read from this slot until we've written the new - // sequence number. WARNING: The next few lines are not reliable on a runtime that - // supports thread aborts. If a thread abort were to sneak in after the CompareExchange - // but before the Volatile.Write, other threads will spin trying to access this slot. - // If this implementation is ever used on such a platform, this if block should be - // wrapped in a finally / prepared region. - if (Interlocked.CompareExchange(ref _headAndTail.Tail, currentTail + 1, currentTail) == currentTail) - { - // Successfully reserved the slot. Note that after the above CompareExchange, other threads - // trying to return will end up spinning until we do the subsequent Write. - _slots[slotsIndex].Item = item; - Volatile.Write(ref _slots[slotsIndex].SequenceNumber, currentTail + 1); - return true; - } - } - else if (diff < 0) - { - // The sequence number was less than what we needed, which means this slot still - // contains a value, i.e. the segment is full. Technically it's possible that multiple - // dequeuers could have read concurrently, with those getting later slots actually - // finishing first, so there could be spaces after this one that are available, but - // we need to enqueue in order. - return false; - } - - // Lost a race. Spin a bit, then try again. - spinner.SpinOnce(); - } - } - - /// <summary>Represents a slot in the queue.</summary> - [StructLayout(LayoutKind.Auto)] - [DebuggerDisplay("Item = {Item}, SequenceNumber = {SequenceNumber}")] - internal struct Slot - { - /// <summary>The item.</summary> - public T Item; - /// <summary>The sequence number for this slot, used to synchronize between enqueuers and dequeuers.</summary> - public int SequenceNumber; - } - } - } - - /// <summary>Padded head and tail indices, to avoid false sharing between producers and consumers.</summary> - [DebuggerDisplay("Head = {Head}, Tail = {Tail}")] - [StructLayout(LayoutKind.Explicit, Size = 384)] // padding before/between/after fields based on worst case cache line size of 128 - internal struct PaddedHeadAndTail - { - [FieldOffset(128)] public int Head; - [FieldOffset(256)] public int Tail; } } diff --git a/src/System.Data.SqlClient/dir.props b/src/System.Data.SqlClient/dir.props index fbff37a79f..2e27354ae7 100644 --- a/src/System.Data.SqlClient/dir.props +++ b/src/System.Data.SqlClient/dir.props @@ -2,7 +2,7 @@ <Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Import Project="..\dir.props" /> <PropertyGroup> - <AssemblyVersion>4.3.1.0</AssemblyVersion> + <AssemblyVersion>4.4.0.0</AssemblyVersion> <AssemblyKey>MSFT</AssemblyKey> <IsUAP>true</IsUAP> </PropertyGroup> diff --git a/src/System.Data.SqlClient/pkg/System.Data.SqlClient.pkgproj b/src/System.Data.SqlClient/pkg/System.Data.SqlClient.pkgproj index d44c77ed26..66578cc337 100644 --- a/src/System.Data.SqlClient/pkg/System.Data.SqlClient.pkgproj +++ b/src/System.Data.SqlClient/pkg/System.Data.SqlClient.pkgproj @@ -3,13 +3,9 @@ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" /> <ItemGroup> <ProjectReference Include="..\ref\System.Data.SqlClient.csproj"> - <SupportedFramework>net461;netcoreapp2.0;$(UAPvNextTFM);$(AllXamarinFrameworks)</SupportedFramework> + <SupportedFramework>net461;netcoreapp2.0;uap10.0.16299;$(UAPvNextTFM);$(AllXamarinFrameworks)</SupportedFramework> </ProjectReference> <ProjectReference Include="..\src\System.Data.SqlClient.csproj" /> - <InboxOnTargetFramework Include="uap10.0.16299" /> - <File Include="$(PlaceHolderFile)"> - <TargetPath>runtimes/win/lib/uap10.0.16299</TargetPath> - </File> <HarvestIncludePaths Include="ref/net451;lib/net451;runtimes/win/lib/net451" /> <HarvestIncludePaths Include="ref/net46;lib/net46;runtimes/win/lib/net46" /> <HarvestIncludePaths Include="ref/netstandard1.2"> @@ -19,6 +15,15 @@ <SupportedFramework>net46;netcoreapp1.0</SupportedFramework> </HarvestIncludePaths> <HarvestIncludePaths Include="runtimes/unix/lib/netstandard1.3;runtimes/win/lib/netstandard1.3" /> + + <!-- Since UAP and .NETCoreApp are package based we still want to enable + OOBing libraries that happen to overlap with their framework package. + This avoids us having to lock the API in our NuGet packages just + to match what shipped inbox: since we can provide a new library + we can update it to add API without raising the netstandard version. --> + <ValidatePackageSuppression Include="TreatAsOutOfBox"> + <Value>.NETCoreApp;UAP</Value> + </ValidatePackageSuppression> </ItemGroup> <ItemGroup> <InboxOnTargetFramework Include="$(AllXamarinFrameworks)" /> diff --git a/src/System.Data.SqlClient/ref/Configurations.props b/src/System.Data.SqlClient/ref/Configurations.props index d9777d8275..c994e0a316 100644 --- a/src/System.Data.SqlClient/ref/Configurations.props +++ b/src/System.Data.SqlClient/ref/Configurations.props @@ -4,6 +4,7 @@ <BuildConfigurations> netstandard; netfx; + netcoreapp; </BuildConfigurations> </PropertyGroup> </Project>
\ No newline at end of file diff --git a/src/System.Data.SqlClient/ref/System.Data.SqlClient.NetCoreApp.cs b/src/System.Data.SqlClient/ref/System.Data.SqlClient.NetCoreApp.cs new file mode 100644 index 0000000000..967da8d258 --- /dev/null +++ b/src/System.Data.SqlClient/ref/System.Data.SqlClient.NetCoreApp.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// ------------------------------------------------------------------------------ +// Changes to this file must follow the http://aka.ms/api-review process. +// ------------------------------------------------------------------------------ + +namespace System.Data.SqlClient +{ + public partial class SqlDataReader : System.Data.Common.IDbColumnSchemaGenerator + { + public System.Collections.ObjectModel.ReadOnlyCollection<System.Data.Common.DbColumn> GetColumnSchema() { throw null; } + } +} diff --git a/src/System.Data.SqlClient/ref/System.Data.SqlClient.cs b/src/System.Data.SqlClient/ref/System.Data.SqlClient.cs index ffe8d62b62..be6f1cf754 100644 --- a/src/System.Data.SqlClient/ref/System.Data.SqlClient.cs +++ b/src/System.Data.SqlClient/ref/System.Data.SqlClient.cs @@ -158,6 +158,7 @@ namespace Microsoft.SqlServer.Server public SqlMetaData(string name, System.Data.SqlDbType dbType, long maxLength, long locale, System.Data.SqlTypes.SqlCompareOptions compareOptions, bool useServerDefault, bool isUniqueKey, System.Data.SqlClient.SortOrder columnSortOrder, int sortOrdinal) { } public SqlMetaData(string name, System.Data.SqlDbType dbType, string database, string owningSchema, string objectName) { } public SqlMetaData(string name, System.Data.SqlDbType dbType, string database, string owningSchema, string objectName, bool useServerDefault, bool isUniqueKey, System.Data.SqlClient.SortOrder columnSortOrder, int sortOrdinal) { } + public SqlMetaData(string name, System.Data.SqlDbType dbType, System.Type userDefinedType) { } public SqlMetaData(string name, System.Data.SqlDbType dbType, System.Type userDefinedType, string serverTypeName) { } public SqlMetaData(string name, System.Data.SqlDbType dbType, System.Type userDefinedType, string serverTypeName, bool useServerDefault, bool isUniqueKey, System.Data.SqlClient.SortOrder columnSortOrder, int sortOrdinal) { } diff --git a/src/System.Data.SqlClient/ref/System.Data.SqlClient.csproj b/src/System.Data.SqlClient/ref/System.Data.SqlClient.csproj index 4388a63c5a..e43848066d 100644 --- a/src/System.Data.SqlClient/ref/System.Data.SqlClient.csproj +++ b/src/System.Data.SqlClient/ref/System.Data.SqlClient.csproj @@ -4,24 +4,35 @@ <PropertyGroup> <ProjectGuid>{D58E8D2B-3331-4660-8DFB-512D66F8EC63}</ProjectGuid> <IsPartialFacadeAssembly Condition="'$(TargetGroup)' == 'netfx'">true</IsPartialFacadeAssembly> - <!-- Must match version supported by frameworks which support 4.3.* inbox. - Can be removed when API is added and this assembly is versioned to 4.4.* --> - <AssemblyVersion Condition="'$(TargetsNetFx)' != 'true'">4.3.1.0</AssemblyVersion> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netfx-Debug|AnyCPU'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netfx-Release|AnyCPU'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Debug|AnyCPU'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Release|AnyCPU'" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Debug|AnyCPU'" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Release|AnyCPU'" /> <ItemGroup> - <SuppressPackageTargetFrameworkCompatibility Include="$(UAPvNextTFM)" /> + <SuppressPackageTargetFrameworkCompatibility Include="$(UAPvNextTFM);uap10.0.16299" /> <Compile Include="System.Data.SqlClient.cs" /> <Compile Include="System.Data.SqlClient.Manual.cs" /> </ItemGroup> + <ItemGroup Condition="'$(TargetGroup)' == 'netcoreapp'"> + <Compile Include="System.Data.SqlClient.NetCoreApp.cs" /> + </ItemGroup> <ItemGroup Condition="'$(TargetGroup)' == 'netfx'"> <Reference Include="mscorlib" /> <Reference Include="System" /> <Reference Include="System.Data" /> <Reference Include="System.Xml" /> </ItemGroup> + <ItemGroup Condition="'$(TargetGroup)' == 'netcoreapp'"> + <ProjectReference Include="..\..\System.Runtime\ref\System.Runtime.csproj" /> + <ProjectReference Include="..\..\System.Runtime.Extensions\ref\System.Runtime.Extensions.csproj" /> + <ProjectReference Include="..\..\System.Data.Common\ref\System.Data.Common.csproj" /> + <ProjectReference Include="..\..\System.Collections.NonGeneric\ref\System.Collections.NonGeneric.csproj" /> + <ProjectReference Include="..\..\System.ComponentModel.Primitives\ref\System.ComponentModel.Primitives.csproj" /> + <ProjectReference Include="..\..\System.Xml.ReaderWriter\ref\System.Xml.ReaderWriter.csproj" /> + <ProjectReference Include="..\..\System.Runtime.InteropServices\ref\System.Runtime.InteropServices.csproj" /> + </ItemGroup> <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> </Project>
\ No newline at end of file diff --git a/src/System.Data.SqlClient/src/Configurations.props b/src/System.Data.SqlClient/src/Configurations.props index abd5922776..3be4b316f4 100644 --- a/src/System.Data.SqlClient/src/Configurations.props +++ b/src/System.Data.SqlClient/src/Configurations.props @@ -8,10 +8,14 @@ netstandard1.3; netstandard; netfx-Windows_NT; + netcoreapp; + netcoreapp-Unix; + netcoreapp-Windows_NT; + uap10.0.16299-Windows_NT; + uap-Windows_NT; </PackageConfigurations> <BuildConfigurations> $(PackageConfigurations) - uap-Windows_NT; </BuildConfigurations> </PropertyGroup> </Project>
\ No newline at end of file diff --git a/src/System.Data.SqlClient/src/MatchingRefApiCompatBaseline.txt b/src/System.Data.SqlClient/src/MatchingRefApiCompatBaseline.txt index 94d2d3a69f..3cd7225c50 100644 --- a/src/System.Data.SqlClient/src/MatchingRefApiCompatBaseline.txt +++ b/src/System.Data.SqlClient/src/MatchingRefApiCompatBaseline.txt @@ -1,5 +1,10 @@ +# +# netstandard dll has been shipped with IDbColumnSchemaGenerator inherited and SqlDataReader.GetColumnScheme() implemented in source, but not exposed in ref contract. +# Removing SqlDataReader.GetColumnScheme() from netstandard implementation potentially breaks existing customer source code +# that utilizes SqlDataReader.GetColumnScheme() indirectly by casting SqlDataReader to IDbColumnSchemaGenerator type. +# In order to prevent it, the API needs to be kept in public, and following 2 error message should be remaining in this baseline file. +# Compat issues with assembly System.Data.SqlClient: -MembersMustExist : Member 'Microsoft.SqlServer.Server.SqlMetaData..ctor(System.String, System.Data.SqlDbType, System.Type)' does not exist in the implementation but it does exist in the contract. CannotRemoveBaseTypeOrInterface : Type 'System.Data.SqlClient.SqlDataReader' does not implement interface 'System.Data.Common.IDbColumnSchemaGenerator' in the implementation but it does in the contract. MembersMustExist : Member 'System.Data.SqlClient.SqlDataReader.GetColumnSchema()' does not exist in the implementation but it does exist in the contract. -Total Issues: 3 +Total Issues: 2 diff --git a/src/System.Data.SqlClient/src/PinvokeAnalyzerExceptionList.analyzerdata.netcoreapp b/src/System.Data.SqlClient/src/PinvokeAnalyzerExceptionList.analyzerdata.netcoreapp new file mode 100644 index 0000000000..5c0648966d --- /dev/null +++ b/src/System.Data.SqlClient/src/PinvokeAnalyzerExceptionList.analyzerdata.netcoreapp @@ -0,0 +1,44 @@ +<!-- Not a bug. sni.dll is a native component that ships as part of the package. --> +kernel32.dll!LoadLibraryExW +sni.dll!GetSniMaxComposedSpnLength +sni.dll!SNIAddProviderWrapper +sni.dll!SNICheckConnectionWrapper +sni.dll!SNICloseWrapper +sni.dll!SNIGetInfoWrapper +sni.dll!SNIGetLastError +sni.dll!SNIInitialize +sni.dll!SNIOpenSyncExWrapper +sni.dll!SNIOpenWrapper +sni.dll!SNIPacketAllocateWrapper +sni.dll!SNIPacketGetDataWrapper +sni.dll!SNIPacketRelease +sni.dll!SNIPacketResetWrapper +sni.dll!SNIPacketSetData +sni.dll!SNIQueryInfo +sni.dll!SNIReadAsyncWrapper +sni.dll!SNIReadSyncOverAsync +sni.dll!SNIRemoveProviderWrapper +sni.dll!SNISecGenClientContextWrapper +sni.dll!SNISecInitPackage +sni.dll!SNISetInfoWrapper +sni.dll!SNITerminate +sni.dll!SNIWaitForSSLHandshakeToCompleteWrapper +sni.dll!SNIWriteAsyncWrapper +sni.dll!SNIWriteSyncOverAsync +sni.dll!UnmanagedIsTokenRestricted +sspicli.dll!AcceptSecurityContext +sspicli.dll!AcquireCredentialsHandleW +sspicli.dll!ApplyControlToken +sspicli.dll!CompleteAuthToken +sspicli.dll!DecryptMessage +sspicli.dll!DeleteSecurityContext +sspicli.dll!EncryptMessage +sspicli.dll!EnumerateSecurityPackagesW +sspicli.dll!FreeContextBuffer +sspicli.dll!FreeCredentialsHandle +sspicli.dll!InitializeSecurityContextW +sspicli.dll!QueryContextAttributesW +sspicli.dll!QuerySecurityContextToken +sspicli.dll!SetContextAttributesW +sspicli.dll!SspiEncodeStringsAsAuthIdentity +sspicli.dll!SspiFreeAuthIdentity
\ No newline at end of file diff --git a/src/System.Data.SqlClient/src/System.Data.SqlClient.csproj b/src/System.Data.SqlClient/src/System.Data.SqlClient.csproj index 7e2717d7ac..00cd509c4e 100644 --- a/src/System.Data.SqlClient/src/System.Data.SqlClient.csproj +++ b/src/System.Data.SqlClient/src/System.Data.SqlClient.csproj @@ -6,6 +6,7 @@ <AssemblyName>System.Data.SqlClient</AssemblyName> <AllowUnsafeBlocks>true</AllowUnsafeBlocks> <IsPartialFacadeAssembly Condition="'$(TargetGroup)' == 'netfx'">true</IsPartialFacadeAssembly> + <IsUAPAssembly Condition="'$(TargetGroup)' == 'uap' OR '$(TargetGroup)' == 'uap10.0.16299'">true</IsUAPAssembly> <GeneratePlatformNotSupportedAssemblyMessage Condition="'$(OSGroup)' == 'AnyOS'">SR.PlatformNotSupported_DataSqlClient</GeneratePlatformNotSupportedAssemblyMessage> <AssemblyVersion Condition="'$(TargetGroup)' == 'netstandard1.2'">4.0.0.0</AssemblyVersion> <AssemblyVersion Condition="'$(TargetGroup)' == 'netstandard1.3'">4.1.0.0</AssemblyVersion> @@ -14,6 +15,8 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netfx-Windows_NT-Release|AnyCPU'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Debug|AnyCPU'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Release|AnyCPU'" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap10.0.16299-Windows_NT-Debug|AnyCPU'" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap10.0.16299-Windows_NT-Release|AnyCPU'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Debug|AnyCPU'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Release|AnyCPU'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Unix-Debug|AnyCPU'" /> @@ -24,7 +27,13 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard1.2-Release|AnyCPU'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard1.3-Debug|AnyCPU'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard1.3-Release|AnyCPU'" /> - <ItemGroup Condition="'$(TargetGroup)' == 'netstandard' OR '$(TargetGroup)' == 'uap' "> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Debug|AnyCPU'" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Release|AnyCPU'" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Unix-Debug|AnyCPU'" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Unix-Release|AnyCPU'" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Windows_NT-Debug|AnyCPU'" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Windows_NT-Release|AnyCPU'" /> + <ItemGroup Condition="'$(TargetGroup)' == 'netstandard' OR '$(TargetGroup)' == 'netcoreapp' OR '$(IsUAPAssembly)' == 'true' "> <Compile Include="System.Data.SqlClient.TypeForwards.cs" /> </ItemGroup> <ItemGroup Condition="'$(IsPartialFacadeAssembly)' != 'true' AND '$(OSGroup)' != 'AnyOS'"> @@ -221,10 +230,10 @@ <Compile Include="System\Data\SqlClient\SqlCredential.cs" /> </ItemGroup> <!-- Manage the SNI toggle for Windows netstandard and UWP --> - <ItemGroup Condition="'$(TargetGroup)' == 'netstandard' AND '$(TargetsWindows)' == 'true'"> + <ItemGroup Condition="('$(TargetGroup)' == 'netstandard' OR '$(TargetGroup)' == 'netcoreapp') AND '$(TargetsWindows)' == 'true'"> <Compile Include="System\Data\SqlClient\TdsParserStateObjectFactory.Windows.cs" /> </ItemGroup> - <ItemGroup Condition="'$(TargetGroup)' == 'uap'"> + <ItemGroup Condition="'$(IsUAPAssembly)' == 'true'"> <Compile Include="System\Data\SqlClient\TdsParserStateObjectFactory.Managed.cs" /> <Compile Include="System\Data\SqlClient\LocalDBAPI.uap.cs" /> <Compile Include="System\Data\SqlClient\SNI\LocalDB.uap.cs" /> @@ -232,7 +241,7 @@ <Compile Include="System\Data\SqlClient\TdsParser.Unix.cs" /> </ItemGroup> <!-- Assets needed on Windows but should be avoided on UAP to avoid sni.dll --> - <ItemGroup Condition=" '$(TargetsWindows)' == 'true' And '$(IsPartialFacadeAssembly)' != 'true' and '$(TargetGroup)' != 'uap' "> + <ItemGroup Condition=" '$(TargetsWindows)' == 'true' And '$(IsPartialFacadeAssembly)' != 'true' and '$(IsUAPAssembly)' != 'true'"> <Compile Include="System\Data\SqlClient\TdsParserStateObjectNative.cs" /> <Compile Include="Interop\SNINativeMethodWrapper.Windows.cs" /> <Compile Include="System\Data\SqlClient\TdsParserSafeHandles.cs" /> @@ -427,7 +436,7 @@ <Compile Include="System\Data\SqlClient\LocalDBAPI.Unix.cs" /> <Compile Include="System\Data\SqlClient\SNI\LocalDB.Unix.cs" /> </ItemGroup> - <ItemGroup Condition="'$(TargetsWindows)' == 'true' And '$(IsPartialFacadeAssembly)' != 'true' and '$(TargetGroup)' != 'uap'"> + <ItemGroup Condition="'$(TargetsWindows)' == 'true' And '$(IsPartialFacadeAssembly)' != 'true' and '$(IsUAPAssembly)' != 'true'"> <Reference Include="Microsoft.Win32.Registry" /> </ItemGroup> <ItemGroup Condition="'$(IsPartialFacadeAssembly)' != 'true'"> @@ -472,7 +481,7 @@ <Reference Include="System.Net.NameResolution" /> <Reference Include="System.Diagnostics.Tracing" /> </ItemGroup> - <ItemGroup Condition="'$(OSGroup)' != 'AnyOS' AND '$(TargetGroup)' == 'uap'"> + <ItemGroup Condition="('$(OSGroup)' != 'AnyOS' AND '$(IsUAPAssembly)' == 'true') OR '$(TargetGroup)' == 'netcoreapp'"> <Reference Include="System.Transactions.Local" /> <Reference Include="System.Collections.NonGeneric" /> </ItemGroup> @@ -485,6 +494,13 @@ <LogicalName>System.Data.SqlClient.SqlMetaData.xml</LogicalName> </EmbeddedResource> </ItemGroup> + <ItemGroup Condition="'$(TargetGroup)' == 'netcoreapp'"> + <Reference Include="System.Runtime.Extensions" /> + <Reference Include="System.Data.Common" /> + <Reference Include="System.ComponentModel.Primitives" /> + <Reference Include="System.Xml.ReaderWriter" /> + <Reference Include="System.Runtime.InteropServices" /> + </ItemGroup> <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> <Import Project=".\GenerateThisAssemblyCs.targets" Condition="'$(IsPartialFacadeAssembly)' != 'true'" /> </Project>
\ No newline at end of file diff --git a/src/System.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs b/src/System.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs index 91716293e9..27758ef22a 100644 --- a/src/System.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs +++ b/src/System.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs @@ -2,12 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; using System.Data.Common; -using System.Diagnostics; using System.Reflection; using System.Security; -using System.Threading; using Xunit; namespace System.Data.SqlClient.Tests @@ -107,35 +104,6 @@ namespace System.Data.SqlClient.Tests Assert.Equal(expected, conn.WorkstationId); } - [Fact] - public void ConnectionTimeoutTestWithThread() - { - int timeoutSec = 5; - string connStrNotAvailable = $"Server=tcp:fakeServer,1433;uid=fakeuser;pwd=fakepwd;Connection Timeout={timeoutSec}"; - - List<ConnectionWorker> list = new List<ConnectionWorker>(); - for (int i = 0; i < 10; ++i) - { - list.Add(new ConnectionWorker(connStrNotAvailable)); - } - - ConnectionWorker.Start(); - ConnectionWorker.Stop(); - - double theMax = 0; - foreach (ConnectionWorker w in list) - { - if (theMax < w.MaxTimeElapsed) - { - theMax = w.MaxTimeElapsed; - } - } - - int threshold = (timeoutSec + 1) * 1000; - - Console.WriteLine($"ConnectionTimeoutTestWithThread: Elapsed Time {theMax} and threshold {threshold}"); - } - [OuterLoop("Can take up to 4 seconds")] [Fact] public void ExceptionsWithMinPoolSizeCanBeHandled() @@ -195,69 +163,5 @@ namespace System.Data.SqlClient.Tests Assert.Equal(sqlCredential, conn.Credential); } - - public class ConnectionWorker - { - private static ManualResetEventSlim startEvent = new ManualResetEventSlim(false); - private static List<ConnectionWorker> workerList = new List<ConnectionWorker>(); - private ManualResetEventSlim doneEvent = new ManualResetEventSlim(false); - private double maxTimeElapsed; - private Thread thread; - private string connectionString; - - public ConnectionWorker(string connectionString) - { - workerList.Add(this); - this.connectionString = connectionString; - thread = new Thread(new ThreadStart(SqlConnectionOpen)); - thread.Start(); - } - - public double MaxTimeElapsed - { - get - { - return maxTimeElapsed; - } - } - - public static void Start() - { - startEvent.Set(); - } - - public static void Stop() - { - foreach (ConnectionWorker w in workerList) - { - w.doneEvent.Wait(); - } - } - - public void SqlConnectionOpen() - { - startEvent.Wait(); - - Stopwatch sw = new Stopwatch(); - using (SqlConnection con = new SqlConnection(connectionString)) - { - sw.Start(); - try - { - con.Open(); - } - catch { } - sw.Stop(); - } - - double elapsed = sw.Elapsed.TotalMilliseconds; - if (maxTimeElapsed < elapsed) - { - maxTimeElapsed = elapsed; - } - - doneEvent.Set(); - } - } } } diff --git a/src/System.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs b/src/System.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs index 94569a4800..7d2a176f3c 100644 --- a/src/System.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs +++ b/src/System.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs @@ -2,6 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; using Xunit; namespace System.Data.SqlClient.ManualTesting.Tests @@ -45,5 +48,102 @@ namespace System.Data.SqlClient.ManualTesting.Tests } Assert.True(false, "No non-empty hostname found for the application"); } + + [CheckConnStrSetupFact] + public static void ConnectionTimeoutTestWithThread() + { + const int timeoutSec = 5; + const int numOfTry = 2; + const int numOfThreads = 5; + + SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(DataTestUtility.TcpConnStr); + builder.DataSource = "invalidhost"; + builder.ConnectTimeout = timeoutSec; + string connStrNotAvailable = builder.ConnectionString; + + for (int i = 0; i < numOfThreads; ++i) + { + new ConnectionWorker(connStrNotAvailable, numOfTry); + } + + ConnectionWorker.Start(); + ConnectionWorker.Stop(); + + double timeTotal = 0; + double timeElapsed = 0; + + foreach (ConnectionWorker w in ConnectionWorker.WorkerList) + { + timeTotal += w.TimeElapsed; + } + timeElapsed = timeTotal / Convert.ToDouble(ConnectionWorker.WorkerList.Count); + + int threshold = timeoutSec * numOfTry * 2 * 1000; + + Assert.True(timeElapsed < threshold); + } + + public class ConnectionWorker + { + private static List<ConnectionWorker> workerList = new List<ConnectionWorker>(); + private ManualResetEventSlim _doneEvent = new ManualResetEventSlim(false); + private double _timeElapsed; + private Thread _thread; + private string _connectionString; + private int _numOfTry; + + public ConnectionWorker(string connectionString, int numOfTry) + { + workerList.Add(this); + _connectionString = connectionString; + _numOfTry = numOfTry; + _thread = new Thread(new ThreadStart(SqlConnectionOpen)); + } + + public static List<ConnectionWorker> WorkerList => workerList; + + public double TimeElapsed => _timeElapsed; + + public static void Start() + { + foreach (ConnectionWorker w in workerList) + { + w._thread.Start(); + } + } + + public static void Stop() + { + foreach (ConnectionWorker w in workerList) + { + w._doneEvent.Wait(); + } + } + + public void SqlConnectionOpen() + { + Stopwatch sw = new Stopwatch(); + double totalTime = 0; + for (int i = 0; i < _numOfTry; ++i) + { + using (SqlConnection con = new SqlConnection(_connectionString)) + { + sw.Start(); + try + { + con.Open(); + } + catch { } + sw.Stop(); + } + totalTime += sw.Elapsed.TotalMilliseconds; + sw.Reset(); + } + + _timeElapsed = totalTime / Convert.ToDouble(_numOfTry); + + _doneEvent.Set(); + } + } } } diff --git a/src/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs b/src/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs index ac99b1825d..5f6db29e67 100644 --- a/src/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs +++ b/src/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs @@ -343,28 +343,21 @@ namespace System.Diagnostics.Tests } [Fact] - public void TestWorkingDirectoryProperty() + public void TestWorkingDirectoryPropertyDefaultCase() { CreateDefaultProcess(); // check defaults Assert.Equal(string.Empty, _process.StartInfo.WorkingDirectory); + } - Process p = CreateProcessLong(); - p.StartInfo.WorkingDirectory = Directory.GetCurrentDirectory(); - - try - { - p.Start(); - Assert.Equal(Directory.GetCurrentDirectory(), p.StartInfo.WorkingDirectory); - } - finally - { - if (!p.HasExited) - p.Kill(); - - Assert.True(p.WaitForExit(WaitInMS)); - } + [Fact] + public void TestWorkingDirectoryPropertyInChildProcess() + { + string workingDirectory = string.IsNullOrEmpty(Environment.SystemDirectory) ? TestDirectory : Environment.SystemDirectory ; + Assert.NotEqual(workingDirectory, Directory.GetCurrentDirectory()); + var psi = new ProcessStartInfo { WorkingDirectory = workingDirectory }; + RemoteInvoke(wd => { Assert.Equal(wd, Directory.GetCurrentDirectory()); return SuccessExitCode; }, workingDirectory, new RemoteInvokeOptions { StartInfo = psi }).Dispose(); } [ActiveIssue(12696)] diff --git a/src/System.Diagnostics.Process/tests/ProcessTests.cs b/src/System.Diagnostics.Process/tests/ProcessTests.cs index aa94bfd54b..fd6641c935 100644 --- a/src/System.Diagnostics.Process/tests/ProcessTests.cs +++ b/src/System.Diagnostics.Process/tests/ProcessTests.cs @@ -1624,15 +1624,15 @@ namespace System.Diagnostics.Tests [SkipOnTargetFramework(TargetFrameworkMonikers.Uap, "Retrieving information about local processes is not supported on uap")] public void Process_StartTest() { - string currentProcessName = GetCurrentProcessName(); + string name = "xcopy.exe"; string userName = string.Empty; string domain = "thisDomain"; SecureString password = AsSecureString("Value"); - using (Process p = Process.Start(currentProcessName, userName, password, domain)) // This writes junk to the Console but with this overload, we can't prevent that. + using (Process p = Process.Start(name, userName, password, domain)) // This writes junk to the Console but with this overload, we can't prevent that. { Assert.NotNull(p); - Assert.Equal(currentProcessName, p.StartInfo.FileName); + Assert.Equal(name, p.StartInfo.FileName); Assert.Equal(userName, p.StartInfo.UserName); Assert.Same(password, p.StartInfo.Password); Assert.Equal(domain, p.StartInfo.Domain); diff --git a/src/System.Drawing.Common/src/System.Drawing.Common.csproj b/src/System.Drawing.Common/src/System.Drawing.Common.csproj index 220c41fc08..24386b8700 100644 --- a/src/System.Drawing.Common/src/System.Drawing.Common.csproj +++ b/src/System.Drawing.Common/src/System.Drawing.Common.csproj @@ -214,6 +214,7 @@ <Compile Include="System\Drawing\Drawing2D\CustomLineCap.Windows.cs" /> <Compile Include="System\Drawing\Drawing2D\GraphicsPath.Windows.cs" /> <Compile Include="System\Drawing\Font.Windows.cs" /> + <Compile Include="System\Drawing\FontFamily.Windows.cs" /> <Compile Include="System\Drawing\GdiplusNative.Windows.cs" /> <Compile Include="System\Drawing\Graphics.Windows.cs" /> <Compile Include="System\Drawing\GraphicsContext.cs" /> @@ -314,6 +315,7 @@ <Compile Include="System\Drawing\BufferedGraphicsContext.Unix.cs" /> <Compile Include="System\Drawing\macFunctions.cs" /> <Compile Include="System\Drawing\Font.Unix.cs" /> + <Compile Include="System\Drawing\FontFamily.Unix.cs" /> <Compile Include="System\Drawing\GdiplusNative.Unix.cs" /> <Compile Include="System\Drawing\GdiPlusStreamHelper.Unix.cs" /> <Compile Include="System\Drawing\LibX11Functions.cs" /> diff --git a/src/System.Drawing.Common/src/System/Drawing/FontFamily.Unix.cs b/src/System.Drawing.Common/src/System/Drawing/FontFamily.Unix.cs new file mode 100644 index 0000000000..277da4cad1 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/FontFamily.Unix.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Drawing +{ + /// <summary> + /// Abstracts a group of type faces having a similar basic design but having certain variation in styles. + /// </summary> + public sealed partial class FontFamily : MarshalByRefObject, IDisposable + { + public override bool Equals(object obj) + { + if (obj == this) + { + return true; + } + + // if obj = null then (obj is FontFamily) = false. + if (!(obj is FontFamily otherFamily)) + { + return false; + } + + // In unix FontFamily objects are not singleton so they don't share the same native pointer, + // the best we have to know if they are the same is FontFamily.Name which gets resolved from the native pointer. + return Name.Equals(otherFamily.Name, StringComparison.Ordinal); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/FontFamily.Windows.cs b/src/System.Drawing.Common/src/System/Drawing/FontFamily.Windows.cs new file mode 100644 index 0000000000..196f58c5d8 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/FontFamily.Windows.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Drawing +{ + /// <summary> + /// Abstracts a group of type faces having a similar basic design but having certain variation in styles. + /// </summary> + public sealed partial class FontFamily : MarshalByRefObject, IDisposable + { + public override bool Equals(object obj) + { + if (obj == this) + { + return true; + } + + // if obj = null then (obj is FontFamily) = false. + if (!(obj is FontFamily otherFamily)) + { + return false; + } + + // We can safely use the ptr to the native GDI+ FontFamily because in windows it is common to + // all objects of the same family (singleton RO object). + return otherFamily.NativeFamily == NativeFamily; + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/FontFamily.cs b/src/System.Drawing.Common/src/System/Drawing/FontFamily.cs index f77dce68a9..4228c63a36 100644 --- a/src/System.Drawing.Common/src/System/Drawing/FontFamily.cs +++ b/src/System.Drawing.Common/src/System/Drawing/FontFamily.cs @@ -14,7 +14,7 @@ namespace System.Drawing /// <summary> /// Abstracts a group of type faces having a similar basic design but having certain variation in styles. /// </summary> - public sealed class FontFamily : MarshalByRefObject, IDisposable + public sealed partial class FontFamily : MarshalByRefObject, IDisposable { private const int NeutralLanguage = 0; private IntPtr _nativeFamily; @@ -132,23 +132,6 @@ namespace System.Drawing internal IntPtr NativeFamily => _nativeFamily; - public override bool Equals(object obj) - { - if (obj == this) - { - return true; - } - - if (!(obj is FontFamily otherFamily)) - { - return false; - } - - // We can safely use the ptr to the native GDI+ FontFamily because it is common to - // all objects of the same family (singleton RO object). - return otherFamily.NativeFamily == NativeFamily; - } - /// <summary> /// Converts this <see cref='FontFamily'/> to a human-readable string. /// </summary> diff --git a/src/System.Drawing.Common/tests/FontTests.cs b/src/System.Drawing.Common/tests/FontTests.cs index da541f5297..5a7b2a3810 100644 --- a/src/System.Drawing.Common/tests/FontTests.cs +++ b/src/System.Drawing.Common/tests/FontTests.cs @@ -16,6 +16,51 @@ namespace System.Drawing.Tests yield return new object[] { FontFamily.GenericSerif, float.MaxValue }; } + public static IEnumerable<object[]> FontFamily_Equals_SameFamily_TestData() + { + yield return new object[] { FontFamily.GenericMonospace, 1 }; + yield return new object[] { FontFamily.GenericSerif, float.MaxValue }; + yield return new object[] { FontFamily.GenericSansSerif, 10 }; + } + + public static IEnumerable<object[]> FontFamily_Equals_DifferentFamily_TestData() + { + yield return new object[] { FontFamily.GenericMonospace, FontFamily.GenericSerif }; + yield return new object[] { FontFamily.GenericSansSerif, FontFamily.GenericSerif }; + yield return new object[] { FontFamily.GenericSansSerif, FontFamily.GenericMonospace }; + } + + [ConditionalTheory(Helpers.GdiplusIsAvailable)] + [MemberData(nameof(FontFamily_Equals_SameFamily_TestData))] + public void Font_Equals_SameFontFamily(FontFamily fontFamily, float size) + { + using (var font1 = new Font(fontFamily, size)) + using (var font2 = new Font(fontFamily, size)) + { + Assert.True(font1.Equals(font2)); + } + } + + [ConditionalTheory(Helpers.GdiplusIsAvailable)] + [MemberData(nameof(FontFamily_Equals_DifferentFamily_TestData))] + public void Font_Equals_DifferentFontFamily(FontFamily fontFamily1, FontFamily fontFamily2) + { + using (var font1 = new Font(fontFamily1, 9)) + using (var font2 = new Font(fontFamily2, 9)) + { + // In some Linux distros all the default fonts live under the same family (Fedora, Centos, Redhat) + if (font1.FontFamily.GetHashCode() != font2.FontFamily.GetHashCode()) + Assert.False(font1.Equals(font2)); + } + } + + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void FontFamily_Equals_NullObject() + { + FontFamily nullFamily = null; + Assert.False(FontFamily.GenericMonospace.Equals(nullFamily)); + } + [ActiveIssue(20884, TestPlatforms.AnyUnix)] [ConditionalTheory(Helpers.GdiplusIsAvailable)] [MemberData(nameof(Ctor_Family_Size_TestData))] diff --git a/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Deflater.cs b/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Deflater.cs index 69c914be7d..11a7d11247 100644 --- a/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Deflater.cs +++ b/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Deflater.cs @@ -91,8 +91,6 @@ namespace System.IO.Compression internal unsafe void SetInput(ReadOnlyMemory<byte> inputBuffer) { Debug.Assert(NeedsInput(), "We have something left in previous input!"); - Debug.Assert(!_inputBufferHandle.HasPointer); - if (0 == inputBuffer.Length) { return; @@ -111,7 +109,6 @@ namespace System.IO.Compression { Debug.Assert(NeedsInput(), "We have something left in previous input!"); Debug.Assert(inputBufferPtr != null); - Debug.Assert(!_inputBufferHandle.HasPointer); if (count == 0) { @@ -182,7 +179,6 @@ namespace System.IO.Compression Debug.Assert(null != outputBuffer, "Can't pass in a null output buffer!"); Debug.Assert(outputBuffer.Length > 0, "Can't pass in an empty output buffer!"); Debug.Assert(NeedsInput(), "We have something left in previous input!"); - Debug.Assert(!_inputBufferHandle.HasPointer); // Note: we require that NeedsInput() == true, i.e. that 0 == _zlibStream.AvailIn. diff --git a/src/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs b/src/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs index a1dde7fb2b..26f6d7f6f3 100644 --- a/src/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs +++ b/src/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs @@ -107,60 +107,66 @@ namespace System.IO.Enumeration if (_lastEntryFound) return false; - do + // If HAVE_READDIR_R is defined for the platform FindNextEntry depends on _entryBuffer being fixed since + // _entry will point to a string in the middle of the array. If the array is not fixed GC can move it after + // the native call and _entry will point to a bogus file name. + fixed (byte* _ = _entryBuffer) { - FindNextEntry(); - if (_lastEntryFound) - return false; + do + { + FindNextEntry(); + if (_lastEntryFound) + return false; - FileAttributes attributes = FileSystemEntry.Initialize( - ref entry, _entry, _currentPath, _rootDirectory, _originalRootDirectory, new Span<char>(_pathBuffer)); - bool isDirectory = (attributes & FileAttributes.Directory) != 0; + FileAttributes attributes = FileSystemEntry.Initialize( + ref entry, _entry, _currentPath, _rootDirectory, _originalRootDirectory, new Span<char>(_pathBuffer)); + bool isDirectory = (attributes & FileAttributes.Directory) != 0; - bool isSpecialDirectory = false; - if (isDirectory) - { - // Subdirectory found - if (_entry.Name[0] == '.' && (_entry.Name[1] == 0 || (_entry.Name[1] == '.' && _entry.Name[2] == 0))) + bool isSpecialDirectory = false; + if (isDirectory) { - // "." or "..", don't process unless the option is set - if (!_options.ReturnSpecialDirectories) - continue; - isSpecialDirectory = true; + // Subdirectory found + if (_entry.Name[0] == '.' && (_entry.Name[1] == 0 || (_entry.Name[1] == '.' && _entry.Name[2] == 0))) + { + // "." or "..", don't process unless the option is set + if (!_options.ReturnSpecialDirectories) + continue; + isSpecialDirectory = true; + } } - } - if (!isSpecialDirectory && _options.AttributesToSkip != 0) - { - if ((_options.AttributesToSkip & FileAttributes.ReadOnly) != 0) + if (!isSpecialDirectory && _options.AttributesToSkip != 0) { - // ReadOnly is the only attribute that requires hitting entry.Attributes (which hits the disk) - attributes = entry.Attributes; + if ((_options.AttributesToSkip & FileAttributes.ReadOnly) != 0) + { + // ReadOnly is the only attribute that requires hitting entry.Attributes (which hits the disk) + attributes = entry.Attributes; + } + + if ((_options.AttributesToSkip & attributes) != 0) + { + continue; + } } - if ((_options.AttributesToSkip & attributes) != 0) + if (isDirectory && !isSpecialDirectory) { - continue; + if (_options.RecurseSubdirectories && ShouldRecurseIntoEntry(ref entry)) + { + // Recursion is on and the directory was accepted, Queue it + if (_pending == null) + _pending = new Queue<string>(); + _pending.Enqueue(Path.Join(_currentPath, entry.FileName)); + } } - } - if (isDirectory && !isSpecialDirectory) - { - if (_options.RecurseSubdirectories && ShouldRecurseIntoEntry(ref entry)) + if (ShouldIncludeEntry(ref entry)) { - // Recursion is on and the directory was accepted, Queue it - if (_pending == null) - _pending = new Queue<string>(); - _pending.Enqueue(Path.Join(_currentPath, entry.FileName)); + _current = TransformEntry(ref entry); + return true; } - } - - if (ShouldIncludeEntry(ref entry)) - { - _current = TransformEntry(ref entry); - return true; - } - } while (true); + } while (true); + } } } diff --git a/src/System.IO.FileSystem/tests/FileStream/ReadWriteSpan.netcoreapp.cs b/src/System.IO.FileSystem/tests/FileStream/ReadWriteSpan.netcoreapp.cs index 0b1898d1f5..187c7788be 100644 --- a/src/System.IO.FileSystem/tests/FileStream/ReadWriteSpan.netcoreapp.cs +++ b/src/System.IO.FileSystem/tests/FileStream/ReadWriteSpan.netcoreapp.cs @@ -189,13 +189,13 @@ namespace System.IO.Tests } [Fact] - public async Task NonEmptyFile_CustomOwnedMemory_ReadAsync_GetsExpectedData() + public async Task NonEmptyFile_CustomMemoryManager_ReadAsync_GetsExpectedData() { string fileName = GetTestFilePath(); File.WriteAllBytes(fileName, TestBuffer); using (var fs = CreateFileStream(fileName, FileMode.Open)) - using (var buffer = new NativeOwnedMemory(TestBuffer.Length)) + using (var buffer = new NativeMemoryManager(TestBuffer.Length)) { Assert.Equal(TestBuffer.Length, await fs.ReadAsync(buffer.Memory)); Assert.Equal<byte>(TestBuffer, buffer.Memory.ToArray()); @@ -255,9 +255,9 @@ namespace System.IO.Tests } [Fact] - public async Task NonEmptyWriteAsync_CustomOwnedMemory_WritesExpectedData() + public async Task NonEmptyWriteAsync_CustomMemoryManager_WritesExpectedData() { - using (var mem = new NativeOwnedMemory(TestBuffer.Length)) + using (var mem = new NativeMemoryManager(TestBuffer.Length)) using (var fs = CreateFileStream(GetTestFilePath(), FileMode.Create)) { new Memory<byte>(TestBuffer).CopyTo(mem.Memory); diff --git a/src/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj b/src/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj index 7597996101..3a25873cb3 100644 --- a/src/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj +++ b/src/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj @@ -177,8 +177,8 @@ <Compile Include="FileInfo\AppendText.cs" /> <Compile Include="FileInfo\CopyTo.cs" /> <!-- Helpers --> - <Compile Include="$(CommonTestPath)\System\Buffers\NativeOwnedMemory.cs"> - <Link>Common\System\Buffers\NativeOwnedMemory.cs</Link> + <Compile Include="$(CommonTestPath)\System\Buffers\NativeMemoryManager.cs"> + <Link>Common\System\Buffers\NativeMemoryManager.cs</Link> </Compile> <Compile Include="$(CommonTestPath)\System\IO\TempFile.cs"> <Link>Common\System\IO\TempFile.cs</Link> diff --git a/src/System.IO.Pipelines/ref/System.IO.Pipelines.cs b/src/System.IO.Pipelines/ref/System.IO.Pipelines.cs index 7f3b7704e1..ba3b46a707 100644 --- a/src/System.IO.Pipelines/ref/System.IO.Pipelines.cs +++ b/src/System.IO.Pipelines/ref/System.IO.Pipelines.cs @@ -29,7 +29,7 @@ namespace System.IO.Pipelines } public partial class PipeOptions { - public PipeOptions(System.Buffers.MemoryPool<byte> pool = null, System.IO.Pipelines.PipeScheduler readerScheduler = null, System.IO.Pipelines.PipeScheduler writerScheduler = null, long pauseWriterThreshold = (long)0, long resumeWriterThreshold = (long)0, int minimumSegmentSize = 2048, bool useSynchronizationContext = true) { } + public PipeOptions(System.Buffers.MemoryPool<byte> pool = null, System.IO.Pipelines.PipeScheduler readerScheduler = null, System.IO.Pipelines.PipeScheduler writerScheduler = null, long pauseWriterThreshold = (long)32768, long resumeWriterThreshold = (long)16384, int minimumSegmentSize = 2048, bool useSynchronizationContext = true) { } public static System.IO.Pipelines.PipeOptions Default { get { throw null; } } public int MinimumSegmentSize { get { throw null; } } public long PauseWriterThreshold { get { throw null; } } diff --git a/src/System.IO.Pipelines/src/System/IO/Pipelines/BufferSegment.cs b/src/System.IO.Pipelines/src/System/IO/Pipelines/BufferSegment.cs index 511e327af0..659aa71638 100644 --- a/src/System.IO.Pipelines/src/System/IO/Pipelines/BufferSegment.cs +++ b/src/System.IO.Pipelines/src/System/IO/Pipelines/BufferSegment.cs @@ -9,7 +9,7 @@ namespace System.IO.Pipelines { internal sealed class BufferSegment : ReadOnlySequenceSegment<byte> { - private OwnedMemory<byte> _ownedMemory; + private IMemoryOwner<byte> _memoryOwner; private BufferSegment _next; private int _end; @@ -53,16 +53,16 @@ namespace System.IO.Pipelines } } - public void SetMemory(OwnedMemory<byte> buffer) + public void SetMemory(IMemoryOwner<byte> memoryOwner) { - SetMemory(buffer, 0, 0); + SetMemory(memoryOwner, 0, 0); } - public void SetMemory(OwnedMemory<byte> ownedMemory, int start, int end, bool readOnly = false) + public void SetMemory(IMemoryOwner<byte> memoryOwner, int start, int end, bool readOnly = false) { - _ownedMemory = ownedMemory; + _memoryOwner = memoryOwner; - AvailableMemory = _ownedMemory.Memory; + AvailableMemory = _memoryOwner.Memory; ReadOnly = readOnly; RunningIndex = 0; @@ -73,12 +73,12 @@ namespace System.IO.Pipelines public void ResetMemory() { - _ownedMemory.Release(); - _ownedMemory = null; + _memoryOwner.Dispose(); + _memoryOwner = null; AvailableMemory = default; } - internal OwnedMemory<byte> OwnedMemory => _ownedMemory; + internal IMemoryOwner<byte> MemoryOwner => _memoryOwner; public Memory<byte> AvailableMemory { get; private set; } diff --git a/src/System.IO.Pipelines/src/System/IO/Pipelines/Pipe.cs b/src/System.IO.Pipelines/src/System/IO/Pipelines/Pipe.cs index 107957c741..96aaefb967 100644 --- a/src/System.IO.Pipelines/src/System/IO/Pipelines/Pipe.cs +++ b/src/System.IO.Pipelines/src/System/IO/Pipelines/Pipe.cs @@ -224,12 +224,12 @@ namespace System.IO.Pipelines } } - internal void CommitUnsynchronized() + internal bool CommitUnsynchronized() { if (_writingHead == null) { // Nothing written to commit - return; + return true; } if (_readHead == null) @@ -241,9 +241,10 @@ namespace System.IO.Pipelines } // Always move the commit head to the write head + var bytesWritten = _currentWriteLength; _commitHead = _writingHead; _commitHeadIndex = _writingHead.End; - _length += _currentWriteLength; + _length += bytesWritten; // Do not reset if reader is complete if (_pauseWriterThreshold > 0 && @@ -256,6 +257,8 @@ namespace System.IO.Pipelines // Clear the writing state _writingHead = null; _currentWriteLength = 0; + + return bytesWritten == 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -296,12 +299,24 @@ namespace System.IO.Pipelines ValueTask<FlushResult> result; lock (_sync) { - CommitUnsynchronized(); + var wasEmpty = CommitUnsynchronized(); // AttachToken before completing reader awaiter in case cancellationToken is already completed cancellationTokenRegistration = _writerAwaitable.AttachToken(cancellationToken, s_signalWriterAwaitable, this); - _readerAwaitable.Complete(out completionData); + // Complete reader only if new data was pushed into the pipe + if (!wasEmpty) + { + _readerAwaitable.Complete(out completionData); + } + else + { + completionData = default; + } + + // I couldn't find a way for flush to induce backpressure deadlock + // if it always adds new data to pipe and wakes up the reader but assert anyway + Debug.Assert(_writerAwaitable.IsCompleted || _readerAwaitable.IsCompleted); // If the writer is completed (which it will be most of the time) the return a completed ValueTask if (_writerAwaitable.IsCompleted) diff --git a/src/System.IO.Pipelines/tests/BackpressureTests.cs b/src/System.IO.Pipelines/tests/BackpressureTests.cs index c35d01193b..a4e67f057d 100644 --- a/src/System.IO.Pipelines/tests/BackpressureTests.cs +++ b/src/System.IO.Pipelines/tests/BackpressureTests.cs @@ -9,10 +9,13 @@ namespace System.IO.Pipelines.Tests { public class BackpressureTests : IDisposable { + private const int PauseWriterThreshold = 64; + private const int ResumeWriterThreshold = 32; + public BackpressureTests() { _pool = new TestMemoryPool(); - _pipe = new Pipe(new PipeOptions(_pool, resumeWriterThreshold: 32, pauseWriterThreshold: 64, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false)); + _pipe = new Pipe(new PipeOptions(_pool, resumeWriterThreshold: ResumeWriterThreshold, pauseWriterThreshold: PauseWriterThreshold, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false)); } public void Dispose() @@ -29,7 +32,7 @@ namespace System.IO.Pipelines.Tests [Fact] public void AdvanceThrowsIfFlushActiveAndNotConsumedPastThreshold() { - PipeWriter writableBuffer = _pipe.Writer.WriteEmpty(64); + PipeWriter writableBuffer = _pipe.Writer.WriteEmpty(PauseWriterThreshold); ValueTask<FlushResult> flushAsync = writableBuffer.FlushAsync(); Assert.False(flushAsync.IsCompleted); @@ -41,9 +44,24 @@ namespace System.IO.Pipelines.Tests } [Fact] + public async Task AdvanceThrowsIfFlushActiveAndNotConsumedPastThresholdWhenFlushIsCanceled() + { + // Write over the threshold and cancel pending flush + _pipe.Writer.WriteEmpty(PauseWriterThreshold); + var task = _pipe.Writer.FlushAsync().AsTask(); + _pipe.Writer.CancelPendingFlush(); + + await task; + + // Examine to the end without releasing backpressure + var result = await _pipe.Reader.ReadAsync(); + Assert.Throws<InvalidOperationException>(() => _pipe.Reader.AdvanceTo(result.Buffer.Start, result.Buffer.End)); + } + + [Fact] public void FlushAsyncAwaitableCompletesWhenReaderAdvancesUnderLow() { - PipeWriter writableBuffer = _pipe.Writer.WriteEmpty(64); + PipeWriter writableBuffer = _pipe.Writer.WriteEmpty(PauseWriterThreshold); ValueTask<FlushResult> flushAsync = writableBuffer.FlushAsync(); Assert.False(flushAsync.IsCompleted); @@ -60,13 +78,13 @@ namespace System.IO.Pipelines.Tests [Fact] public void FlushAsyncAwaitableDoesNotCompletesWhenReaderAdvancesUnderHight() { - PipeWriter writableBuffer = _pipe.Writer.WriteEmpty(64); + PipeWriter writableBuffer = _pipe.Writer.WriteEmpty(PauseWriterThreshold); ValueTask<FlushResult> flushAsync = writableBuffer.FlushAsync(); Assert.False(flushAsync.IsCompleted); ReadResult result = _pipe.Reader.ReadAsync().GetAwaiter().GetResult(); - SequencePosition consumed = result.Buffer.GetPosition(32); + SequencePosition consumed = result.Buffer.GetPosition(ResumeWriterThreshold); _pipe.Reader.AdvanceTo(consumed, consumed); Assert.False(flushAsync.IsCompleted); @@ -75,7 +93,7 @@ namespace System.IO.Pipelines.Tests [Fact] public void FlushAsyncAwaitableResetsOnCommit() { - PipeWriter writableBuffer = _pipe.Writer.WriteEmpty(64); + PipeWriter writableBuffer = _pipe.Writer.WriteEmpty(PauseWriterThreshold); ValueTask<FlushResult> flushAsync = writableBuffer.FlushAsync(); Assert.False(flushAsync.IsCompleted); @@ -88,7 +106,7 @@ namespace System.IO.Pipelines.Tests FlushResult flushResult = flushAsync.GetAwaiter().GetResult(); Assert.False(flushResult.IsCompleted); - writableBuffer = _pipe.Writer.WriteEmpty(64); + writableBuffer = _pipe.Writer.WriteEmpty(PauseWriterThreshold); flushAsync = writableBuffer.FlushAsync(); Assert.False(flushAsync.IsCompleted); @@ -97,7 +115,7 @@ namespace System.IO.Pipelines.Tests [Fact] public void FlushAsyncReturnsCompletedIfReaderCompletes() { - PipeWriter writableBuffer = _pipe.Writer.WriteEmpty(64); + PipeWriter writableBuffer = _pipe.Writer.WriteEmpty(PauseWriterThreshold); ValueTask<FlushResult> flushAsync = writableBuffer.FlushAsync(); Assert.False(flushAsync.IsCompleted); @@ -108,7 +126,7 @@ namespace System.IO.Pipelines.Tests FlushResult flushResult = flushAsync.GetAwaiter().GetResult(); Assert.True(flushResult.IsCompleted); - writableBuffer = _pipe.Writer.WriteEmpty(64); + writableBuffer = _pipe.Writer.WriteEmpty(PauseWriterThreshold); flushAsync = writableBuffer.FlushAsync(); flushResult = flushAsync.GetAwaiter().GetResult(); @@ -119,7 +137,7 @@ namespace System.IO.Pipelines.Tests [Fact] public async Task FlushAsyncReturnsCompletedIfReaderCompletesWithoutAdvance() { - PipeWriter writableBuffer = _pipe.Writer.WriteEmpty(64); + PipeWriter writableBuffer = _pipe.Writer.WriteEmpty(PauseWriterThreshold); ValueTask<FlushResult> flushAsync = writableBuffer.FlushAsync(); Assert.False(flushAsync.IsCompleted); @@ -131,7 +149,7 @@ namespace System.IO.Pipelines.Tests FlushResult flushResult = flushAsync.GetAwaiter().GetResult(); Assert.True(flushResult.IsCompleted); - writableBuffer = _pipe.Writer.WriteEmpty(64); + writableBuffer = _pipe.Writer.WriteEmpty(PauseWriterThreshold); flushAsync = writableBuffer.FlushAsync(); flushResult = flushAsync.GetAwaiter().GetResult(); @@ -142,7 +160,7 @@ namespace System.IO.Pipelines.Tests [Fact] public void FlushAsyncReturnsCompletedTaskWhenSizeLessThenLimit() { - PipeWriter writableBuffer = _pipe.Writer.WriteEmpty(32); + PipeWriter writableBuffer = _pipe.Writer.WriteEmpty(ResumeWriterThreshold); ValueTask<FlushResult> flushAsync = writableBuffer.FlushAsync(); Assert.True(flushAsync.IsCompleted); FlushResult flushResult = flushAsync.GetAwaiter().GetResult(); @@ -152,7 +170,7 @@ namespace System.IO.Pipelines.Tests [Fact] public void FlushAsyncReturnsNonCompletedSizeWhenCommitOverTheLimit() { - PipeWriter writableBuffer = _pipe.Writer.WriteEmpty(64); + PipeWriter writableBuffer = _pipe.Writer.WriteEmpty(PauseWriterThreshold); ValueTask<FlushResult> flushAsync = writableBuffer.FlushAsync(); Assert.False(flushAsync.IsCompleted); } @@ -162,7 +180,7 @@ namespace System.IO.Pipelines.Tests { _pipe.Reader.Complete(new InvalidOperationException("Reader failed")); - PipeWriter writableBuffer = _pipe.Writer.WriteEmpty(64); + PipeWriter writableBuffer = _pipe.Writer.WriteEmpty(PauseWriterThreshold); InvalidOperationException invalidOperationException = await Assert.ThrowsAsync<InvalidOperationException>(async () => await writableBuffer.FlushAsync()); Assert.Equal("Reader failed", invalidOperationException.Message); diff --git a/src/System.IO.Pipelines/tests/PipePoolTests.cs b/src/System.IO.Pipelines/tests/PipePoolTests.cs index 8e8779accc..f4faa4bd86 100644 --- a/src/System.IO.Pipelines/tests/PipePoolTests.cs +++ b/src/System.IO.Pipelines/tests/PipePoolTests.cs @@ -12,28 +12,25 @@ namespace System.IO.Pipelines.Tests { private class DisposeTrackingBufferPool : TestMemoryPool { - public int ReturnedBlocks { get; set; } public int DisposedBlocks { get; set; } public int CurrentlyRentedBlocks { get; set; } - public override OwnedMemory<byte> Rent(int size) + public override IMemoryOwner<byte> Rent(int size) { - return new DisposeTrackingOwnedMemory(new byte[size], this); + return new DisposeTrackingMemoryManager(new byte[size], this); } protected override void Dispose(bool disposing) { } - private class DisposeTrackingOwnedMemory : OwnedMemory<byte> + private class DisposeTrackingMemoryManager : MemoryManager<byte> { private byte[] _array; private readonly DisposeTrackingBufferPool _bufferPool; - private int _refCount = 1; - - public DisposeTrackingOwnedMemory(byte[] array, DisposeTrackingBufferPool bufferPool) + public DisposeTrackingMemoryManager(byte[] array, DisposeTrackingBufferPool bufferPool) { _array = array; _bufferPool = bufferPool; @@ -42,21 +39,14 @@ namespace System.IO.Pipelines.Tests public override int Length => _array.Length; - public override Span<byte> Span + public bool IsDisposed => _array == null; + + public override MemoryHandle Pin(int elementIndex = 0) { - get - { - if (IsDisposed) - throw new ObjectDisposedException(nameof(DisposeTrackingBufferPool)); - return _array; - } + throw new NotImplementedException(); } - public override bool IsDisposed => _array == null; - - protected override bool IsRetained => _refCount > 0; - - public override MemoryHandle Pin(int byteOffset = 0) + public override void Unpin() { throw new NotImplementedException(); } @@ -71,26 +61,17 @@ namespace System.IO.Pipelines.Tests protected override void Dispose(bool disposing) { - if (IsRetained) - { - throw new InvalidOperationException(); - } _bufferPool.DisposedBlocks++; + _bufferPool.CurrentlyRentedBlocks--; _array = null; } - public override bool Release() + public override Span<byte> GetSpan() { - _bufferPool.ReturnedBlocks++; - _bufferPool.CurrentlyRentedBlocks--; - _refCount--; - return IsRetained; - } - - public override void Retain() - { - _refCount++; + if (IsDisposed) + throw new ObjectDisposedException(nameof(DisposeTrackingBufferPool)); + return _array; } } } @@ -113,8 +94,7 @@ namespace System.IO.Pipelines.Tests pipe.Reader.AdvanceTo(readResult.Buffer.End); Assert.Equal(0, pool.CurrentlyRentedBlocks); - Assert.Equal(0, pool.DisposedBlocks); - Assert.Equal(3, pool.ReturnedBlocks); + Assert.Equal(3, pool.DisposedBlocks); } [Fact] @@ -143,8 +123,7 @@ namespace System.IO.Pipelines.Tests await pipe.Writer.WriteAsync(new byte[writeSize]); Assert.Equal(1, pool.CurrentlyRentedBlocks); - Assert.Equal(0, pool.DisposedBlocks); - Assert.Equal(2, pool.ReturnedBlocks); + Assert.Equal(2, pool.DisposedBlocks); } [Fact] @@ -157,13 +136,11 @@ namespace System.IO.Pipelines.Tests readerWriter.Writer.Complete(); readerWriter.Reader.Complete(); - Assert.Equal(1, pool.ReturnedBlocks); - Assert.Equal(0, pool.DisposedBlocks); + Assert.Equal(1, pool.DisposedBlocks); readerWriter.Writer.Complete(); readerWriter.Reader.Complete(); - Assert.Equal(1, pool.ReturnedBlocks); - Assert.Equal(0, pool.DisposedBlocks); + Assert.Equal(1, pool.DisposedBlocks); } [Fact] @@ -198,8 +175,7 @@ namespace System.IO.Pipelines.Tests pipe.Reader.Complete(); pipe.Writer.Complete(); Assert.Equal(0, pool.CurrentlyRentedBlocks); - Assert.Equal(1, pool.ReturnedBlocks); - Assert.Equal(0, pool.DisposedBlocks); + Assert.Equal(1, pool.DisposedBlocks); } [Fact] @@ -213,8 +189,7 @@ namespace System.IO.Pipelines.Tests pipe.Reader.Complete(); pipe.Writer.Complete(); Assert.Equal(0, pool.CurrentlyRentedBlocks); - Assert.Equal(2, pool.ReturnedBlocks); - Assert.Equal(0, pool.DisposedBlocks); + Assert.Equal(2, pool.DisposedBlocks); } [Fact] diff --git a/src/System.IO.Pipelines/tests/PipeReaderWriterFacts.cs b/src/System.IO.Pipelines/tests/PipeReaderWriterFacts.cs index 6010fd5b1f..f25f08b2a2 100644 --- a/src/System.IO.Pipelines/tests/PipeReaderWriterFacts.cs +++ b/src/System.IO.Pipelines/tests/PipeReaderWriterFacts.cs @@ -35,7 +35,7 @@ namespace System.IO.Pipelines.Tests private readonly TestMemoryPool _pool; [Fact] - public async Task AdvanceEmptyBufferAfterWritingResetsAwaitable() + public async Task CanReadAndWrite() { byte[] bytes = Encoding.ASCII.GetBytes("Hello World"); @@ -50,14 +50,6 @@ namespace System.IO.Pipelines.Tests Assert.Equal("Hello World", Encoding.ASCII.GetString(array)); _pipe.Reader.AdvanceTo(buffer.End); - - // Now write 0 and advance 0 - await _pipe.Writer.WriteAsync(new byte[] { }); - result = await _pipe.Reader.ReadAsync(); - _pipe.Reader.AdvanceTo(result.Buffer.End); - - ValueTask<ReadResult> awaitable = _pipe.Reader.ReadAsync(); - Assert.False(awaitable.IsCompleted); } [Fact] @@ -75,8 +67,7 @@ namespace System.IO.Pipelines.Tests ValueTask<ReadResult> awaitable = _pipe.Reader.ReadAsync(); Assert.False(awaitable.IsCompleted); - // Unblock without writing anything - _pipe.Writer.GetMemory(); + _pipe.Writer.Write(new byte[1]); await _pipe.Writer.FlushAsync(); Assert.True(awaitable.IsCompleted); @@ -153,7 +144,7 @@ namespace System.IO.Pipelines.Tests [Fact] public async Task CompleteReaderAfterFlushWithoutAdvancingDoesNotThrow() { - await _pipe.Writer.FlushAsync(); + await _pipe.Writer.WriteAsync(new byte[10]); ReadResult result = await _pipe.Reader.ReadAsync(); ReadOnlySequence<byte> buffer = result.Buffer; @@ -178,20 +169,20 @@ namespace System.IO.Pipelines.Tests var startSegment = (BufferSegment)start; var endSegment = (BufferSegment)end; - Assert.NotNull(startSegment.OwnedMemory); - Assert.NotNull(endSegment.OwnedMemory); + Assert.NotNull(startSegment.MemoryOwner); + Assert.NotNull(endSegment.MemoryOwner); _pipe.Reader.Complete(); // Nothing cleaned up - Assert.NotNull(startSegment.OwnedMemory); - Assert.NotNull(endSegment.OwnedMemory); + Assert.NotNull(startSegment.MemoryOwner); + Assert.NotNull(endSegment.MemoryOwner); _pipe.Writer.Complete(); // Should be cleaned up now - Assert.Null(startSegment.OwnedMemory); - Assert.Null(endSegment.OwnedMemory); + Assert.Null(startSegment.MemoryOwner); + Assert.Null(endSegment.MemoryOwner); _pipe.Reset(); } @@ -210,33 +201,6 @@ namespace System.IO.Pipelines.Tests } [Fact] - public async Task EmptyBufferStartCrossingSegmentBoundaryIsTreatedLikeAndEnd() - { - Memory<byte> memory = _pipe.Writer.GetMemory(); - // Append one full segment to a pipe - _pipe.Writer.Write(memory.Span); - await _pipe.Writer.FlushAsync(); - - // Consume entire segment - ReadResult result = await _pipe.Reader.ReadAsync(); - _pipe.Reader.AdvanceTo(result.Buffer.End); - - // Append empty segment - _pipe.Writer.GetMemory(1); - await _pipe.Writer.FlushAsync(); - - result = await _pipe.Reader.ReadAsync(); - - Assert.True(result.Buffer.IsEmpty); - Assert.Equal(result.Buffer.Start, result.Buffer.End); - - _pipe.Writer.GetMemory(); - _pipe.Reader.AdvanceTo(result.Buffer.Start); - ValueTask<ReadResult> awaitable = _pipe.Reader.ReadAsync(); - Assert.False(awaitable.IsCompleted); - } - - [Fact] public void FlushAsync_ReturnsCompletedTaskWhenMaxSizeIfZero() { PipeWriter writableBuffer = _pipe.Writer.WriteEmpty(1); @@ -808,6 +772,30 @@ namespace System.IO.Pipelines.Tests Assert.True(IsTaskWithResult(task)); } + [Fact] + public void EmptyFlushAsyncDoesntWakeUpReader() + { + ValueTask<ReadResult> task = _pipe.Reader.ReadAsync(); + _pipe.Writer.FlushAsync(); + + Assert.False(task.IsCompleted); + } + + [Fact] + public async Task EmptyFlushAsyncDoesntWakeUpReaderAfterAdvance() + { + await _pipe.Writer.WriteAsync(new byte[10]); + + ReadResult result = await _pipe.Reader.ReadAsync(); + _pipe.Reader.AdvanceTo(result.Buffer.Start, result.Buffer.End); + + ValueTask<ReadResult> task = _pipe.Reader.ReadAsync(); + + await _pipe.Writer.FlushAsync(); + + Assert.False(task.IsCompleted); + } + private bool IsTaskWithResult<T>(ValueTask<T> task) { return task == new ValueTask<T>(task.Result); diff --git a/src/System.IO.Pipelines/tests/PipeWriterTests.cs b/src/System.IO.Pipelines/tests/PipeWriterTests.cs index 4ac05668b7..d60841d673 100644 --- a/src/System.IO.Pipelines/tests/PipeWriterTests.cs +++ b/src/System.IO.Pipelines/tests/PipeWriterTests.cs @@ -77,18 +77,6 @@ namespace System.IO.Pipelines.Tests } [Fact] - public void CanWriteEmpty() - { - PipeWriter writer = Pipe.Writer; - var array = new byte[] { }; - - writer.Write(array); - writer.Write(new Span<byte>(array, 0, array.Length)); - - Assert.Equal(array, Read()); - } - - [Fact] public void CanWriteIntoHeadlessBuffer() { PipeWriter writer = Pipe.Writer; @@ -130,6 +118,8 @@ namespace System.IO.Pipelines.Tests var span = writer.GetSpan(10); Assert.True(span.Length >= 10); + // 0 byte Flush would not complete the reader so we complete. + Pipe.Writer.Complete(); Assert.Equal(new byte[] { }, Read()); } diff --git a/src/System.IO.Pipelines/tests/TestMemoryPool.cs b/src/System.IO.Pipelines/tests/TestMemoryPool.cs index 0736c37876..3b01df3ca3 100644 --- a/src/System.IO.Pipelines/tests/TestMemoryPool.cs +++ b/src/System.IO.Pipelines/tests/TestMemoryPool.cs @@ -15,10 +15,10 @@ namespace System.IO.Pipelines private bool _disposed; - public override OwnedMemory<byte> Rent(int minBufferSize = -1) + public override IMemoryOwner<byte> Rent(int minBufferSize = -1) { CheckDisposed(); - return new PooledMemory(_pool.Rent(minBufferSize), this); + return new PooledMemory((MemoryManager<byte>)_pool.Rent(minBufferSize), this); } protected override void Dispose(bool disposing) @@ -36,9 +36,9 @@ namespace System.IO.Pipelines } } - private class PooledMemory : OwnedMemory<byte> + private class PooledMemory : MemoryManager<byte> { - private OwnedMemory<byte> _ownedMemory; + private MemoryManager<byte> _manager; private readonly TestMemoryPool _pool; @@ -48,9 +48,9 @@ namespace System.IO.Pipelines private string _leaser; - public PooledMemory(OwnedMemory<byte> ownedMemory, TestMemoryPool pool) + public PooledMemory(MemoryManager<byte> manager, TestMemoryPool pool) { - _ownedMemory = ownedMemory; + _manager = manager; _pool = pool; _leaser = Environment.StackTrace; _referenceCount = 1; @@ -64,26 +64,19 @@ namespace System.IO.Pipelines protected override void Dispose(bool disposing) { _pool.CheckDisposed(); - _ownedMemory.Dispose(); } - public override MemoryHandle Pin(int byteOffset = 0) + public override MemoryHandle Pin(int elementIndex = 0) { _pool.CheckDisposed(); - return _ownedMemory.Pin(byteOffset); - } - - public override void Retain() - { - _pool.CheckDisposed(); - _ownedMemory.Retain(); Interlocked.Increment(ref _referenceCount); + return _manager.Pin(elementIndex); } - public override bool Release() + public override void Unpin() { _pool.CheckDisposed(); - _ownedMemory.Release(); + _manager.Unpin(); int newRefCount = Interlocked.Decrement(ref _referenceCount); @@ -93,33 +86,13 @@ namespace System.IO.Pipelines if (newRefCount == 0) { _returned = true; - return false; } - return true; } protected override bool TryGetArray(out ArraySegment<byte> segment) { _pool.CheckDisposed(); - return MemoryMarshal.TryGetArray(_ownedMemory.Memory, out segment); - } - - public override bool IsDisposed - { - get - { - _pool.CheckDisposed(); - return _ownedMemory.IsDisposed; - } - } - - protected override bool IsRetained - { - get - { - _pool.CheckDisposed(); - return !_returned; - } + return MemoryMarshal.TryGetArray(_manager.Memory, out segment); } public override int Length @@ -127,17 +100,14 @@ namespace System.IO.Pipelines get { _pool.CheckDisposed(); - return _ownedMemory.Length; + return _manager.Length; } } - public override Span<byte> Span + public override Span<byte> GetSpan() { - get - { - _pool.CheckDisposed(); - return _ownedMemory.Span; - } + _pool.CheckDisposed(); + return _manager.GetSpan(); } } } diff --git a/src/System.IO/tests/Stream/Stream.ReadWriteSpan.netcoreapp.cs b/src/System.IO/tests/Stream/Stream.ReadWriteSpan.netcoreapp.cs index 6bdea544b0..c25418c06f 100644 --- a/src/System.IO/tests/Stream/Stream.ReadWriteSpan.netcoreapp.cs +++ b/src/System.IO/tests/Stream/Stream.ReadWriteSpan.netcoreapp.cs @@ -115,7 +115,7 @@ namespace System.IO.Tests return Task.FromResult(10); }); - using (var totalNativeMemory = new NativeOwnedMemory(30)) + using (var totalNativeMemory = new NativeMemoryManager(30)) { Memory<byte> totalMemory = totalNativeMemory.Memory; Memory<byte> targetMemory = totalMemory.Slice(5, 20); @@ -174,7 +174,7 @@ namespace System.IO.Tests return Task.CompletedTask; }); - using (var nativeMemory = new NativeOwnedMemory(10)) + using (var nativeMemory = new NativeMemoryManager(10)) { Memory<byte> memory = nativeMemory.Memory; memory.Span[2] = 0; diff --git a/src/System.IO/tests/System.IO.Tests.csproj b/src/System.IO/tests/System.IO.Tests.csproj index 52cf08256d..a1a7f7cc49 100644 --- a/src/System.IO/tests/System.IO.Tests.csproj +++ b/src/System.IO/tests/System.IO.Tests.csproj @@ -57,8 +57,8 @@ <Compile Include="StringReader\StringReader.CtorTests.cs" /> <Compile Include="StringWriter\StringWriterTests.netcoreapp.cs" Condition="'$(TargetGroup)' == 'netcoreapp'" /> <Compile Include="StringWriter\StringWriterTests.cs" /> - <Compile Include="$(CommonTestPath)\System\Buffers\NativeOwnedMemory.cs" Condition="'$(TargetGroup)' == 'netcoreapp'"> - <Link>Common\System\Buffers\NativeOwnedMemory.cs</Link> + <Compile Include="$(CommonTestPath)\System\Buffers\NativeMemoryManager.cs" Condition="'$(TargetGroup)' == 'netcoreapp'"> + <Link>Common\System\Buffers\NativeMemoryManager.cs</Link> </Compile> <Compile Include="$(CommonTestPath)\System\IO\CallTrackingStream.cs"> <Link>Common\System\IO\CallTrackingStream.cs</Link> diff --git a/src/System.Memory/ref/System.Memory.cs b/src/System.Memory/ref/System.Memory.cs index 9775637231..9f750abd53 100644 --- a/src/System.Memory/ref/System.Memory.cs +++ b/src/System.Memory/ref/System.Memory.cs @@ -9,15 +9,15 @@ namespace System { public static partial class MemoryExtensions { + public static System.ReadOnlyMemory<char> AsMemory(this string text) { throw null; } + public static System.ReadOnlyMemory<char> AsMemory(this string text, int start) { throw null; } + public static System.ReadOnlyMemory<char> AsMemory(this string text, int start, int length) { throw null; } public static System.Memory<T> AsMemory<T>(this System.ArraySegment<T> segment) { throw null; } public static System.Memory<T> AsMemory<T>(this System.ArraySegment<T> segment, int start) { throw null; } public static System.Memory<T> AsMemory<T>(this System.ArraySegment<T> segment, int start, int length) { throw null; } public static System.Memory<T> AsMemory<T>(this T[] array) { throw null; } public static System.Memory<T> AsMemory<T>(this T[] array, int start) { throw null; } public static System.Memory<T> AsMemory<T>(this T[] array, int start, int length) { throw null; } - public static System.ReadOnlyMemory<char> AsMemory(this string text) { throw null; } - public static System.ReadOnlyMemory<char> AsMemory(this string text, int start) { throw null; } - public static System.ReadOnlyMemory<char> AsMemory(this string text, int start, int length) { throw null; } public static System.ReadOnlySpan<char> AsSpan(this string text) { throw null; } public static System.ReadOnlySpan<char> AsSpan(this string text, int start) { throw null; } public static System.ReadOnlySpan<char> AsSpan(this string text, int start, int length) { throw null; } @@ -92,6 +92,7 @@ namespace System public readonly partial struct Memory<T> { private readonly object _dummy; + public Memory(System.Buffers.MemoryManager<T> manager, int start, int length) { throw null; } public Memory(T[] array) { throw null; } public Memory(T[] array, int start, int length) { throw null; } public static System.Memory<T> Empty { get { throw null; } } @@ -99,6 +100,8 @@ namespace System public int Length { get { throw null; } } public System.Span<T> Span { get { throw null; } } public void CopyTo(System.Memory<T> destination) { } + [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] + public static System.Memory<T> CreateFromPinnedArray(T[] array, int start, int length) { throw null; } public bool Equals(System.Memory<T> other) { throw null; } [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] public override bool Equals(object obj) { throw null; } @@ -108,7 +111,6 @@ namespace System public static implicit operator System.ReadOnlyMemory<T> (System.Memory<T> memory) { throw null; } public static implicit operator System.Memory<T> (T[] array) { throw null; } public System.Buffers.MemoryHandle Pin() { throw null; } - public System.Buffers.MemoryHandle Retain(bool pin = false) { throw null; } public System.Memory<T> Slice(int start) { throw null; } public System.Memory<T> Slice(int start, int length) { throw null; } public T[] ToArray() { throw null; } @@ -133,7 +135,6 @@ namespace System public static implicit operator System.ReadOnlyMemory<T> (System.ArraySegment<T> segment) { throw null; } public static implicit operator System.ReadOnlyMemory<T> (T[] array) { throw null; } public System.Buffers.MemoryHandle Pin() { throw null; } - public System.Buffers.MemoryHandle Retain(bool pin = false) { throw null; } public System.ReadOnlyMemory<T> Slice(int start) { throw null; } public System.ReadOnlyMemory<T> Slice(int start, int length) { throw null; } public T[] ToArray() { throw null; } @@ -178,18 +179,18 @@ namespace System public readonly partial struct SequencePosition : System.IEquatable<System.SequencePosition> { private readonly object _dummy; - public SequencePosition(object segment, int index) { throw null; } - [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] - public int GetInteger() { throw null; } - [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] - public object GetObject() { throw null; } + public SequencePosition(object @object, int integer) { throw null; } [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] public override bool Equals(object obj) { throw null; } public bool Equals(System.SequencePosition other) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] public override int GetHashCode() { throw null; } + [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] + public int GetInteger() { throw null; } + [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] + public object GetObject() { throw null; } public static bool operator ==(System.SequencePosition left, System.SequencePosition right) { throw null; } public static bool operator !=(System.SequencePosition left, System.SequencePosition right) { throw null; } - public override string ToString() { throw null; } } public readonly ref partial struct Span<T> { @@ -232,33 +233,49 @@ namespace System } namespace System.Buffers { + public static partial class BuffersExtensions + { + public static void CopyTo<T>(this in System.Buffers.ReadOnlySequence<T> source, System.Span<T> destination) { } + public static System.Nullable<System.SequencePosition> PositionOf<T>(this in System.Buffers.ReadOnlySequence<T> source, T value) where T : System.IEquatable<T> { throw null; } + public static T[] ToArray<T>(this in System.Buffers.ReadOnlySequence<T> sequence) { throw null; } + public static void Write<T>(this System.Buffers.IBufferWriter<T> writer, System.ReadOnlySpan<T> value) { } + } public partial interface IBufferWriter<T> { void Advance(int count); System.Memory<T> GetMemory(int sizeHint = 0); System.Span<T> GetSpan(int sizeHint = 0); } - public abstract partial class ReadOnlySequenceSegment<T> + public partial interface IMemoryOwner<T> : System.IDisposable { - public System.ReadOnlyMemory<T> Memory { get; protected set; } - public System.Buffers.ReadOnlySequenceSegment<T> Next { get; protected set; } - public long RunningIndex { get; protected set; } + System.Memory<T> Memory { get; } } - public partial interface IRetainable + public partial interface IPinnable { - bool Release(); - void Retain(); + System.Buffers.MemoryHandle Pin(int elementIndex); + void Unpin(); } public partial struct MemoryHandle : System.IDisposable { private object _dummy; [System.CLSCompliantAttribute(false)] - public unsafe MemoryHandle(System.Buffers.IRetainable retainable, void* pointer = null, System.Runtime.InteropServices.GCHandle handle = default(System.Runtime.InteropServices.GCHandle)) { throw null; } - public bool HasPointer { get { throw null; } } + public unsafe MemoryHandle(void* pointer, System.Runtime.InteropServices.GCHandle handle = default(System.Runtime.InteropServices.GCHandle), System.Buffers.IPinnable pinnable = null) { throw null; } [System.CLSCompliantAttribute(false)] public unsafe void* Pointer { get { throw null; } } public void Dispose() { } } + public abstract partial class MemoryManager<T> : System.Buffers.IMemoryOwner<T>, System.Buffers.IPinnable, System.IDisposable + { + protected MemoryManager() { } + public virtual int Length { get { throw null; } } + public virtual System.Memory<T> Memory { get { throw null; } } + protected abstract void Dispose(bool disposing); + public abstract System.Span<T> GetSpan(); + public abstract System.Buffers.MemoryHandle Pin(int elementIndex = 0); + void System.IDisposable.Dispose() { } + protected internal virtual bool TryGetArray(out System.ArraySegment<T> segment) { throw null; } + public abstract void Unpin(); + } public abstract partial class MemoryPool<T> : System.IDisposable { protected MemoryPool() { } @@ -266,7 +283,7 @@ namespace System.Buffers public static System.Buffers.MemoryPool<T> Shared { get { throw null; } } public void Dispose() { } protected abstract void Dispose(bool disposing); - public abstract System.Buffers.OwnedMemory<T> Rent(int minBufferSize = -1); + public abstract System.Buffers.IMemoryOwner<T> Rent(int minBufferSize = -1); } public enum OperationStatus { @@ -275,38 +292,21 @@ namespace System.Buffers InvalidData = 3, NeedMoreData = 2, } - public abstract partial class OwnedMemory<T> : System.Buffers.IRetainable, System.IDisposable - { - protected OwnedMemory() { } - public abstract bool IsDisposed { get; } - protected abstract bool IsRetained { get; } - public abstract int Length { get; } - public System.Memory<T> Memory { get { throw null; } } - public abstract System.Span<T> Span { get; } - public void Dispose() { } - protected abstract void Dispose(bool disposing); - public abstract System.Buffers.MemoryHandle Pin(int byteOffset = 0); - public abstract bool Release(); - public abstract void Retain(); - protected internal abstract bool TryGetArray(out System.ArraySegment<T> segment); - } - public static partial class BuffersExtensions + public abstract partial class ReadOnlySequenceSegment<T> { - public static void CopyTo<T>(in this System.Buffers.ReadOnlySequence<T> source, System.Span<T> destination) { } - public static System.Nullable<System.SequencePosition> PositionOf<T>(in this System.Buffers.ReadOnlySequence<T> source, T value) where T : System.IEquatable<T> { throw null; } - public static T[] ToArray<T>(in this System.Buffers.ReadOnlySequence<T> sequence) { throw null; } - public static void Write<T>(this System.Buffers.IBufferWriter<T> writer, ReadOnlySpan<T> value) { } + protected ReadOnlySequenceSegment() { } + public System.ReadOnlyMemory<T> Memory { get { throw null; } protected set { } } + public System.Buffers.ReadOnlySequenceSegment<T> Next { get { throw null; } protected set { } } + public long RunningIndex { get { throw null; } protected set { } } } public readonly partial struct ReadOnlySequence<T> { private readonly object _dummy; public static readonly System.Buffers.ReadOnlySequence<T> Empty; public ReadOnlySequence(System.Buffers.ReadOnlySequenceSegment<T> startSegment, int startIndex, System.Buffers.ReadOnlySequenceSegment<T> endSegment, int endIndex) { throw null; } + public ReadOnlySequence(System.ReadOnlyMemory<T> memory) { throw null; } public ReadOnlySequence(T[] array) { throw null; } public ReadOnlySequence(T[] array, int start, int length) { throw null; } - public ReadOnlySequence(System.ReadOnlyMemory<T> memory) { throw null; } - public ReadOnlySequence(System.Buffers.OwnedMemory<T> memory) { throw null; } - public ReadOnlySequence(System.Buffers.OwnedMemory<T> memory, int start, int length) { throw null; } public System.SequencePosition End { get { throw null; } } public System.ReadOnlyMemory<T> First { get { throw null; } } public bool IsEmpty { get { throw null; } } @@ -522,21 +522,18 @@ namespace System.Runtime.InteropServices public static T Read<T>(System.ReadOnlySpan<byte> source) where T : struct { throw null; } public static System.Collections.Generic.IEnumerable<T> ToEnumerable<T>(System.ReadOnlyMemory<T> memory) { throw null; } public static bool TryGetArray<T>(System.ReadOnlyMemory<T> memory, out System.ArraySegment<T> segment) { throw null; } - public static bool TryGetOwnedMemory<T, TOwner>(ReadOnlyMemory<T> memory, out TOwner owner) - where TOwner : System.Buffers.OwnedMemory<T> { throw null; } - public static bool TryGetOwnedMemory<T, TOwner>(ReadOnlyMemory<T> memory, out TOwner owner, out int start, out int length) - where TOwner : System.Buffers.OwnedMemory<T> { throw null; } + public static bool TryGetMemoryManager<T, TManager>(System.ReadOnlyMemory<T> memory, out TManager manager) where TManager : System.Buffers.MemoryManager<T> { throw null; } + public static bool TryGetMemoryManager<T, TManager>(System.ReadOnlyMemory<T> memory, out TManager manager, out int start, out int length) where TManager : System.Buffers.MemoryManager<T> { throw null; } public static bool TryGetString(System.ReadOnlyMemory<char> memory, out string text, out int start, out int length) { throw null; } public static bool TryRead<T>(System.ReadOnlySpan<byte> source, out T value) where T : struct { throw null; } public static bool TryWrite<T>(System.Span<byte> destination, ref T value) where T : struct { throw null; } public static void Write<T>(System.Span<byte> destination, ref T value) where T : struct { } } - public static partial class SequenceMarshal { public static bool TryGetArray<T>(System.Buffers.ReadOnlySequence<T> sequence, out System.ArraySegment<T> segment) { throw null; } - public static bool TryGetReadOnlySequenceSegment<T>(System.Buffers.ReadOnlySequence<T> sequence, out System.Buffers.ReadOnlySequenceSegment<T> startSegment, out int startIndex, out System.Buffers.ReadOnlySequenceSegment<T> endSegment, out int endIndex) { throw null; } - public static bool TryGetOwnedMemory<T>(System.Buffers.ReadOnlySequence<T> sequence, out System.Buffers.OwnedMemory<T> ownedMemory, out int start, out int length) { throw null; } + public static bool TryGetMemoryManager<T>(System.Buffers.ReadOnlySequence<T> sequence, out System.Buffers.MemoryManager<T> manager, out int start, out int length) { throw null; } public static bool TryGetReadOnlyMemory<T>(System.Buffers.ReadOnlySequence<T> sequence, out System.ReadOnlyMemory<T> memory) { throw null; } + public static bool TryGetReadOnlySequenceSegment<T>(System.Buffers.ReadOnlySequence<T> sequence, out System.Buffers.ReadOnlySequenceSegment<T> startSegment, out int startIndex, out System.Buffers.ReadOnlySequenceSegment<T> endSegment, out int endIndex) { throw null; } } } diff --git a/src/System.Memory/src/System.Memory.csproj b/src/System.Memory/src/System.Memory.csproj index 5e364ecf6b..dda3aac4c1 100644 --- a/src/System.Memory/src/System.Memory.csproj +++ b/src/System.Memory/src/System.Memory.csproj @@ -116,9 +116,10 @@ <Compile Include="$(CommonPath)\CoreLib\System\SpanHelpers.Byte.cs" /> <Compile Include="$(CommonPath)\CoreLib\System\SpanHelpers.Char.cs" /> <Compile Include="$(CommonPath)\CoreLib\System\SpanHelpers.T.cs" /> - <Compile Include="$(CommonPath)\CoreLib\System\Buffers\IRetainable.cs" /> + <Compile Include="$(CommonPath)\CoreLib\System\Buffers\IPinnable.cs" /> + <Compile Include="$(CommonPath)\CoreLib\System\Buffers\IMemoryOwner.cs" /> <Compile Include="$(CommonPath)\CoreLib\System\Buffers\MemoryHandle.cs" /> - <Compile Include="$(CommonPath)\CoreLib\System\Buffers\OwnedMemory.cs" /> + <Compile Include="$(CommonPath)\CoreLib\System\Buffers\MemoryManager.cs" /> <Compile Include="$(CommonPath)\CoreLib\System\Runtime\InteropServices\MemoryMarshal.cs" /> <Compile Include="System\ReadOnlySpan.Portable.cs" /> <Compile Include="System\Span.Portable.cs" /> diff --git a/src/System.Memory/src/System/Buffers/ArrayMemoryPool.ArrayMemoryPoolBuffer.cs b/src/System.Memory/src/System/Buffers/ArrayMemoryPool.ArrayMemoryPoolBuffer.cs index e3db9db89d..6e94654423 100644 --- a/src/System.Memory/src/System/Buffers/ArrayMemoryPool.ArrayMemoryPoolBuffer.cs +++ b/src/System.Memory/src/System/Buffers/ArrayMemoryPool.ArrayMemoryPoolBuffer.cs @@ -15,7 +15,7 @@ namespace System.Buffers { internal sealed partial class ArrayMemoryPool<T> : MemoryPool<T> { - private sealed class ArrayMemoryPoolBuffer : OwnedMemory<T> + private sealed class ArrayMemoryPoolBuffer : MemoryManager<T> { private T[] _array; private int _refCount; @@ -28,19 +28,16 @@ namespace System.Buffers public sealed override int Length => _array.Length; - public sealed override bool IsDisposed => _array == null; + public bool IsDisposed => _array == null; - protected sealed override bool IsRetained => Volatile.Read(ref _refCount) > 0; + public bool IsRetained => Volatile.Read(ref _refCount) > 0; - public sealed override Span<T> Span + public sealed override Span<T> GetSpan() { - get - { - if (IsDisposed) - ThrowHelper.ThrowObjectDisposedException_ArrayMemoryPoolBuffer(); + if (IsDisposed) + ThrowHelper.ThrowObjectDisposedException_ArrayMemoryPoolBuffer(); - return _array; - } + return _array; } protected sealed override void Dispose(bool disposing) @@ -67,44 +64,39 @@ namespace System.Buffers return true; } - public sealed override MemoryHandle Pin(int byteOffset = 0) + public sealed override MemoryHandle Pin(int elementIndex = 0) { unsafe { - Retain(); // this checks IsDisposed + while (true) + { + int currentCount = Volatile.Read(ref _refCount); + if (currentCount <= 0) + ThrowHelper.ThrowObjectDisposedException_ArrayMemoryPoolBuffer(); + if (Interlocked.CompareExchange(ref _refCount, currentCount + 1, currentCount) == currentCount) + break; + } try { - if ((IntPtr.Size == 4 && (uint)byteOffset > (uint)_array.Length * (uint)Unsafe.SizeOf<T>()) - || (IntPtr.Size != 4 && (ulong)byteOffset > (uint)_array.Length * (ulong)Unsafe.SizeOf<T>())) + if ((uint)elementIndex > (uint)_array.Length) { - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.byteOffset); + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.elementIndex); } GCHandle handle = GCHandle.Alloc(_array, GCHandleType.Pinned); - return new MemoryHandle(this, ((byte*)handle.AddrOfPinnedObject()) + byteOffset, handle); + + return new MemoryHandle(Unsafe.Add<T>(((void*)handle.AddrOfPinnedObject()), elementIndex), handle, this); } catch { - Release(); + Unpin(); throw; } } } - public sealed override void Retain() - { - while (true) - { - int currentCount = Volatile.Read(ref _refCount); - if (currentCount <= 0) - ThrowHelper.ThrowObjectDisposedException_ArrayMemoryPoolBuffer(); - if (Interlocked.CompareExchange(ref _refCount, currentCount + 1, currentCount) == currentCount) - break; - } - } - - public sealed override bool Release() + public sealed override void Unpin() { while (true) { @@ -115,10 +107,9 @@ namespace System.Buffers { if (currentCount == 1) { - Dispose(); - return false; + Dispose(true); } - return true; + break; } } } diff --git a/src/System.Memory/src/System/Buffers/ArrayMemoryPool.cs b/src/System.Memory/src/System/Buffers/ArrayMemoryPool.cs index 1882c5e723..5d8e0911d9 100644 --- a/src/System.Memory/src/System/Buffers/ArrayMemoryPool.cs +++ b/src/System.Memory/src/System/Buffers/ArrayMemoryPool.cs @@ -15,7 +15,7 @@ namespace System.Buffers private const int s_maxBufferSize = int.MaxValue; public sealed override int MaxBufferSize => s_maxBufferSize; - public sealed override OwnedMemory<T> Rent(int minimumBufferSize = -1) + public sealed override IMemoryOwner<T> Rent(int minimumBufferSize = -1) { if (minimumBufferSize == -1) minimumBufferSize = 1 + (4095 / Unsafe.SizeOf<T>()); diff --git a/src/System.Memory/src/System/Buffers/MemoryPool.cs b/src/System.Memory/src/System/Buffers/MemoryPool.cs index febe988ccc..72dc890ecd 100644 --- a/src/System.Memory/src/System/Buffers/MemoryPool.cs +++ b/src/System.Memory/src/System/Buffers/MemoryPool.cs @@ -20,7 +20,7 @@ namespace System.Buffers /// Returns a memory block capable of holding at least <paramref name="minBufferSize" /> elements of T. /// </summary> /// <param name="minBufferSize">If -1 is passed, this is set to a default value for the pool.</param> - public abstract OwnedMemory<T> Rent(int minBufferSize = -1); + public abstract IMemoryOwner<T> Rent(int minBufferSize = -1); /// <summary> /// Returns the maximum buffer size supported by this pool. diff --git a/src/System.Memory/src/System/Buffers/ReadOnlySequence.cs b/src/System.Memory/src/System/Buffers/ReadOnlySequence.cs index 64ccf639d9..bdd590ddd0 100644 --- a/src/System.Memory/src/System/Buffers/ReadOnlySequence.cs +++ b/src/System.Memory/src/System/Buffers/ReadOnlySequence.cs @@ -119,10 +119,10 @@ namespace System.Buffers /// </summary> public ReadOnlySequence(ReadOnlyMemory<T> memory) { - if (MemoryMarshal.TryGetOwnedMemory(memory, out OwnedMemory<T> ownedMemory, out int index, out int length)) + if (MemoryMarshal.TryGetMemoryManager(memory, out MemoryManager<T> manager, out int index, out int length)) { - _sequenceStart = new SequencePosition(ownedMemory, ReadOnlySequence.OwnedMemoryToSequenceStart(index)); - _sequenceEnd = new SequencePosition(ownedMemory, ReadOnlySequence.OwnedMemoryToSequenceEnd(length)); + _sequenceStart = new SequencePosition(manager, ReadOnlySequence.MemoryManagerToSequenceStart(index)); + _sequenceEnd = new SequencePosition(manager, ReadOnlySequence.MemoryManagerToSequenceEnd(length)); } else if (MemoryMarshal.TryGetArray(memory, out ArraySegment<T> segment)) { @@ -149,34 +149,6 @@ namespace System.Buffers } /// <summary> - /// Creates an instance of <see cref="ReadOnlySequence{T}"/> from the <see cref="OwnedMemory{T}"/>. - /// Consumer is expected to manage lifetime of memory until <see cref="ReadOnlySequence{T}"/> is not used anymore. - /// </summary> - public ReadOnlySequence(OwnedMemory<T> ownedMemory) - { - if (ownedMemory == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.ownedMemory); - - _sequenceStart = new SequencePosition(ownedMemory, ReadOnlySequence.OwnedMemoryToSequenceStart(0)); - _sequenceEnd = new SequencePosition(ownedMemory, ReadOnlySequence.OwnedMemoryToSequenceEnd(ownedMemory.Length)); - } - - /// <summary> - /// Creates an instance of <see cref="ReadOnlySequence{T}"/> from the <see cref="OwnedMemory{T}"/>, start and length. - /// Consumer is expected to manage lifetime of memory until <see cref="ReadOnlySequence{T}"/> is not used anymore. - /// </summary> - public ReadOnlySequence(OwnedMemory<T> ownedMemory, int start, int length) - { - if (ownedMemory == null || - (uint)start > (uint)ownedMemory.Length || - (uint)length > (uint)(ownedMemory.Length - start)) - ThrowHelper.ThrowArgumentValidationException(ownedMemory, start); - - _sequenceStart = new SequencePosition(ownedMemory, ReadOnlySequence.OwnedMemoryToSequenceStart(start)); - _sequenceEnd = new SequencePosition(ownedMemory, ReadOnlySequence.OwnedMemoryToSequenceEnd(start + length)); - } - - /// <summary> /// Forms a slice out of the given <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/>, with <paramref name="length"/> items /// </summary> /// <param name="start">The index at which to begin this slice.</param> @@ -411,7 +383,7 @@ namespace System.Buffers { MultiSegment = 0x00, Array = 0x1, - OwnedMemory = 0x2, + MemoryManager = 0x2, String = 0x3, Empty = 0x4 } @@ -428,8 +400,8 @@ namespace System.Buffers public const int ArrayStartMask = 0; public const int ArrayEndMask = FlagBitMask; - public const int OwnedMemoryStartMask = FlagBitMask; - public const int OwnedMemoryEndMask = 0; + public const int MemoryManagerStartMask = FlagBitMask; + public const int MemoryManagerEndMask = 0; public const int StringStartMask = FlagBitMask; public const int StringEndMask = FlagBitMask; @@ -443,9 +415,9 @@ namespace System.Buffers [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ArrayToSequenceEnd(int endIndex) => endIndex | ArrayEndMask; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int OwnedMemoryToSequenceStart(int startIndex) => startIndex | OwnedMemoryStartMask; + public static int MemoryManagerToSequenceStart(int startIndex) => startIndex | MemoryManagerStartMask; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int OwnedMemoryToSequenceEnd(int endIndex) => endIndex | OwnedMemoryEndMask; + public static int MemoryManagerToSequenceEnd(int endIndex) => endIndex | MemoryManagerEndMask; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int StringToSequenceStart(int startIndex) => startIndex | StringStartMask; [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/System.Memory/src/System/Buffers/ReadOnlySequence_helpers.cs b/src/System.Memory/src/System/Buffers/ReadOnlySequence_helpers.cs index 44c60e8e10..f3f40bf838 100644 --- a/src/System.Memory/src/System/Buffers/ReadOnlySequence_helpers.cs +++ b/src/System.Memory/src/System/Buffers/ReadOnlySequence_helpers.cs @@ -60,11 +60,11 @@ namespace System.Buffers data = new ReadOnlyMemory<T>(Unsafe.As<T[]>(startObject)); } - else if (type == SequenceType.OwnedMemory) + else if (type == SequenceType.MemoryManager) { - Debug.Assert(startObject is OwnedMemory<T>); + Debug.Assert(startObject is MemoryManager<T>); - data = (Unsafe.As<OwnedMemory<T>>(startObject)).Memory; + data = (Unsafe.As<MemoryManager<T>>(startObject)).Memory; } else if (typeof(T) == typeof(char) && type == SequenceType.String) { @@ -121,11 +121,11 @@ namespace System.Buffers memory = new ReadOnlyMemory<T>(Unsafe.As<T[]>(startObject)); } - else if (type == SequenceType.OwnedMemory) + else if (type == SequenceType.MemoryManager) { - Debug.Assert(startObject is OwnedMemory<T>); + Debug.Assert(startObject is MemoryManager<T>); - memory = (Unsafe.As<OwnedMemory<T>>(startObject)).Memory; + memory = (Unsafe.As<MemoryManager<T>>(startObject)).Memory; } else if (typeof(T) == typeof(char) && type == SequenceType.String) { @@ -158,7 +158,7 @@ namespace System.Buffers var startSegment = Unsafe.As<ReadOnlySequenceSegment<T>>(startObject); int currentLength = startSegment.Memory.Length - startIndex; - + // Position in start segment, defer to single segment seek if (currentLength > count) goto IsSingleSegment; @@ -199,7 +199,7 @@ namespace System.Buffers // Hit the end of the segments but didn't reach the count if (currentSegment == null || (currentSegment == endObject && endPosition < count)) ThrowHelper.ThrowArgumentOutOfRangeException_CountOutOfRange(); - + FoundSegment: return new SequencePosition(currentSegment, (int)count); } @@ -322,21 +322,21 @@ namespace System.Buffers return true; } - internal bool TryGetOwnedMemory(out OwnedMemory<T> ownedMemory, out int start, out int length) + internal bool TryGetMemoryManager(out MemoryManager<T> manager, out int start, out int length) { GetTypeAndIndices(Start.GetInteger(), End.GetInteger(), out SequenceType type, out start, out int endIndex); - if (type != SequenceType.OwnedMemory) + if (type != SequenceType.MemoryManager) { - ownedMemory = default; + manager = default; length = 0; return false; } Debug.Assert(Start.GetObject() != null); - Debug.Assert(Start.GetObject() is OwnedMemory<T>); + Debug.Assert(Start.GetObject() is MemoryManager<T>); - ownedMemory = Unsafe.As<OwnedMemory<T>>(Start.GetObject()); + manager = Unsafe.As<MemoryManager<T>>(Start.GetObject()); length = endIndex - start; return true; } @@ -364,11 +364,11 @@ namespace System.Buffers memory = new ReadOnlyMemory<T>(Unsafe.As<T[]>(startObject)); } - else if (type == SequenceType.OwnedMemory) + else if (type == SequenceType.MemoryManager) { - Debug.Assert(startObject is OwnedMemory<T>); + Debug.Assert(startObject is MemoryManager<T>); - memory = Unsafe.As<OwnedMemory<T>>(startObject).Memory; + memory = Unsafe.As<MemoryManager<T>>(startObject).Memory; } else if (typeof(T) == typeof(char) && type == SequenceType.String) { diff --git a/src/System.Memory/src/System/Runtime/InteropServices/SequenceMarshal.cs b/src/System.Memory/src/System/Runtime/InteropServices/SequenceMarshal.cs index e3710b1493..420748468d 100644 --- a/src/System.Memory/src/System/Runtime/InteropServices/SequenceMarshal.cs +++ b/src/System.Memory/src/System/Runtime/InteropServices/SequenceMarshal.cs @@ -34,12 +34,12 @@ namespace System.Runtime.InteropServices } /// <summary> - /// Get <see cref="OwnedMemory{T}"/> from the underlying <see cref="ReadOnlySequence{T}"/>. - /// If unable to get the <see cref="OwnedMemory{T}"/>, return false. + /// Get <see cref="MemoryManager{T}"/> from the underlying <see cref="ReadOnlySequence{T}"/>. + /// If unable to get the <see cref="MemoryManager{T}"/>, return false. /// </summary> - public static bool TryGetOwnedMemory<T>(ReadOnlySequence<T> sequence, out OwnedMemory<T> ownedMemory, out int start, out int length) + public static bool TryGetMemoryManager<T>(ReadOnlySequence<T> sequence, out MemoryManager<T> manager, out int start, out int length) { - return sequence.TryGetOwnedMemory(out ownedMemory, out start, out length); + return sequence.TryGetMemoryManager(out manager, out start, out length); } /// <summary> diff --git a/src/System.Memory/src/System/ThrowHelper.cs b/src/System.Memory/src/System/ThrowHelper.cs index 75a486ace0..5c39e4f93f 100644 --- a/src/System.Memory/src/System/ThrowHelper.cs +++ b/src/System.Memory/src/System/ThrowHelper.cs @@ -88,10 +88,6 @@ namespace System [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateObjectDisposedException_ArrayMemoryPoolBuffer() { return new ObjectDisposedException("ArrayMemoryPoolBuffer"); } - internal static void ThrowObjectDisposedException_MemoryDisposed() { throw CreateObjectDisposedException_MemoryDisposed(); } - [MethodImpl(MethodImplOptions.NoInlining)] - private static Exception CreateObjectDisposedException_MemoryDisposed() { return new ObjectDisposedException("OwnedMemory<T>", SR.MemoryDisposed); } - internal static void ThrowFormatException_BadFormatSpecifier() { throw CreateFormatException_BadFormatSpecifier(); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateFormatException_BadFormatSpecifier() { return new FormatException(SR.Argument_BadFormatSpecifier); } @@ -155,19 +151,6 @@ namespace System else return CreateArgumentOutOfRangeException(ExceptionArgument.length); } - - public static void ThrowArgumentValidationException<T>(OwnedMemory<T> ownedMemory, int start) - => throw CreateArgumentValidationException(ownedMemory, start); - - private static Exception CreateArgumentValidationException<T>(OwnedMemory<T> ownedMemory, int start) - { - if (ownedMemory == null) - return CreateArgumentNullException(ExceptionArgument.ownedMemory); - else if ((uint)start > (uint)ownedMemory.Length) - return CreateArgumentOutOfRangeException(ExceptionArgument.start); - else - return CreateArgumentOutOfRangeException(ExceptionArgument.length); - } } #if !MONO internal enum ExceptionArgument @@ -175,7 +158,7 @@ namespace System length, start, minimumBufferSize, - byteOffset, + elementIndex, comparable, comparer, destination, @@ -185,7 +168,6 @@ namespace System startIndex, endIndex, array, - ownedMemory, culture } #endif diff --git a/src/System.Memory/tests/Memory/CtorArray.cs b/src/System.Memory/tests/Memory/CtorArray.cs index f7122f3d73..fe60e41ffb 100644 --- a/src/System.Memory/tests/Memory/CtorArray.cs +++ b/src/System.Memory/tests/Memory/CtorArray.cs @@ -75,7 +75,7 @@ namespace System.MemoryTests memory.Validate(); Assert.Equal(default, memory); - memory = new Memory<int>(null, 0, 0); + memory = new Memory<int>((int[])null, 0, 0); memory.Validate(); Assert.Equal(default, memory); } @@ -83,10 +83,10 @@ namespace System.MemoryTests [Fact] public static void CtorArrayNullArrayNonZeroStartAndLength() { - Assert.Throws<ArgumentOutOfRangeException>(() => new Memory<int>(null, 1, 0)); - Assert.Throws<ArgumentOutOfRangeException>(() => new Memory<int>(null, 0, 1)); - Assert.Throws<ArgumentOutOfRangeException>(() => new Memory<int>(null, 1, 1)); - Assert.Throws<ArgumentOutOfRangeException>(() => new Memory<int>(null, -1, -1)); + Assert.Throws<ArgumentOutOfRangeException>(() => new Memory<int>((int[])null, 1, 0)); + Assert.Throws<ArgumentOutOfRangeException>(() => new Memory<int>((int[])null, 0, 1)); + Assert.Throws<ArgumentOutOfRangeException>(() => new Memory<int>((int[])null, 1, 1)); + Assert.Throws<ArgumentOutOfRangeException>(() => new Memory<int>((int[])null, -1, -1)); } [Fact] diff --git a/src/System.Memory/tests/Memory/CustomMemoryForTest.cs b/src/System.Memory/tests/Memory/CustomMemoryForTest.cs index 2692bee41d..5afebf3832 100644 --- a/src/System.Memory/tests/Memory/CustomMemoryForTest.cs +++ b/src/System.Memory/tests/Memory/CustomMemoryForTest.cs @@ -9,7 +9,7 @@ using System.Threading; namespace System.MemoryTests { - public class CustomMemoryForTest<T> : OwnedMemory<T> + public class CustomMemoryForTest<T> : MemoryManager<T> { private bool _disposed; private int _referenceCount; @@ -18,7 +18,7 @@ namespace System.MemoryTests private readonly int _offset; private readonly int _length; - public CustomMemoryForTest(T[] array): this(array, 0, array.Length) + public CustomMemoryForTest(T[] array) : this(array, 0, array.Length) { } @@ -33,40 +33,38 @@ namespace System.MemoryTests public override int Length => _length; - public override bool IsDisposed => _disposed; + public bool IsDisposed => _disposed; - protected override bool IsRetained => _referenceCount > 0; + protected bool IsRetained => _referenceCount > 0; - public override Span<T> Span + public override Span<T> GetSpan() { - get - { - if (IsDisposed) - throw new ObjectDisposedException(nameof(CustomMemoryForTest<T>)); - return new Span<T>(_array, _offset, _length); - } + if (IsDisposed) + throw new ObjectDisposedException(nameof(CustomMemoryForTest<T>)); + return new Span<T>(_array, _offset, _length); } - public override MemoryHandle Pin(int byteOffset = 0) + public override MemoryHandle Pin(int elementIndex = 0) { unsafe { - Retain(); // this checks IsDisposed + if (IsDisposed) + throw new ObjectDisposedException(nameof(CustomMemoryForTest<T>)); + Interlocked.Increment(ref _referenceCount); try { - if ((IntPtr.Size == 4 && (uint)byteOffset > (uint)_array.Length * (uint)Unsafe.SizeOf<T>()) - || (IntPtr.Size != 4 && (ulong)byteOffset > (uint)_array.Length * (ulong)Unsafe.SizeOf<T>())) + if ((uint)elementIndex > (uint)(_array.Length - _offset)) { - throw new ArgumentOutOfRangeException(nameof(byteOffset)); + throw new ArgumentOutOfRangeException(nameof(elementIndex)); } var handle = GCHandle.Alloc(_array, GCHandleType.Pinned); - return new MemoryHandle(this, Unsafe.Add<byte>((void*)handle.AddrOfPinnedObject(), _offset + byteOffset), handle); + return new MemoryHandle(Unsafe.Add<T>((void*)handle.AddrOfPinnedObject(), _offset + elementIndex), handle, this); } catch { - Release(); + Unpin(); throw; } } @@ -94,14 +92,7 @@ namespace System.MemoryTests } - public override void Retain() - { - if (IsDisposed) - throw new ObjectDisposedException(nameof(CustomMemoryForTest<T>)); - Interlocked.Increment(ref _referenceCount); - } - - public override bool Release() + public override void Unpin() { int newRefCount = Interlocked.Decrement(ref _referenceCount); @@ -111,9 +102,7 @@ namespace System.MemoryTests if (newRefCount == 0) { _noReferencesCalledCount++; - return false; } - return true; } } } diff --git a/src/System.Memory/tests/Memory/OwnedMemory.cs b/src/System.Memory/tests/Memory/MemoryManager.cs index d75e164b40..4f6f2461cf 100644 --- a/src/System.Memory/tests/Memory/OwnedMemory.cs +++ b/src/System.Memory/tests/Memory/MemoryManager.cs @@ -8,16 +8,16 @@ using Xunit; namespace System.MemoryTests { // - // Tests for internal Memory<T>.ctor(OwnedMemory<T>, int , int) + // Tests for Memory<T>.ctor(MemoryManager<T>, int , int) // public static partial class MemoryTests { [Fact] - public static void MemoryFromOwnedMemoryInt() + public static void MemoryFromMemoryManagerInt() { int[] a = { 91, 92, -93, 94 }; - OwnedMemory<int> owner = new CustomMemoryForTest<int>(a); - Memory<int> memory = owner.Memory; + MemoryManager<int> manager = new CustomMemoryForTest<int>(a); + Memory<int> memory = manager.Memory; memory.Validate(91, 92, -93, 94); memory.Slice(0, 4).Validate(91, 92, -93, 94); memory.Slice(1, 0).Validate(); @@ -28,11 +28,11 @@ namespace System.MemoryTests } [Fact] - public static void ReadOnlyMemoryFromMemoryFromOwnedMemoryInt() + public static void ReadOnlyMemoryFromMemoryFromMemoryManagerInt() { int[] a = { 91, 92, -93, 94 }; - OwnedMemory<int> owner = new CustomMemoryForTest<int>(a); - ReadOnlyMemory<int> readOnlyMemory = owner.Memory; + MemoryManager<int> manager = new CustomMemoryForTest<int>(a); + ReadOnlyMemory<int> readOnlyMemory = manager.Memory; readOnlyMemory.Validate(91, 92, -93, 94); readOnlyMemory.Slice(0, 4).Validate(91, 92, -93, 94); readOnlyMemory.Slice(1, 0).Validate(); @@ -43,11 +43,11 @@ namespace System.MemoryTests } [Fact] - public static void MemoryFromOwnedMemoryLong() + public static void MemoryFromMemoryManagerLong() { long[] a = { 91, -92, 93, 94, -95 }; - OwnedMemory<long> owner = new CustomMemoryForTest<long>(a); - Memory<long> memory = owner.Memory; + MemoryManager<long> manager = new CustomMemoryForTest<long>(a); + Memory<long> memory = manager.Memory; memory.Validate(91, -92, 93, 94, -95); memory.Slice(0, 5).Validate(91, -92, 93, 94, -95); memory.Slice(1, 0).Validate(); @@ -58,58 +58,63 @@ namespace System.MemoryTests } [Fact] - public static void MemoryFromOwnedMemoryObject() + public static void MemoryFromMemoryManagerObject() { object o1 = new object(); object o2 = new object(); object[] a = { o1, o2 }; - OwnedMemory<object> owner = new CustomMemoryForTest<object>(a); - Memory<object> memory = owner.Memory; + MemoryManager<object> manager = new CustomMemoryForTest<object>(a); + Memory<object> memory = manager.Memory; memory.ValidateReferenceType(o1, o2); } [Fact] - public static void ImplicitReadOnlyMemoryFromOwnedMemory() + public static void ImplicitReadOnlyMemoryFromMemoryManager() { long[] a = { 91, -92, 93, 94, -95 }; - OwnedMemory<long> owner = new CustomMemoryForTest<long>(a); - Memory<long> memory = owner.Memory; + MemoryManager<long> manager = new CustomMemoryForTest<long>(a); + Memory<long> memory = manager.Memory; CastReadOnly<long>(memory, 91, -92, 93, 94, -95); } [Fact] - public static void OwnedMemoryDispose() + public static void MemoryManagerDispose() { int[] a = { 91, 92, -93, 94 }; - OwnedMemory<int> owner = new CustomMemoryForTest<int>(a); - Assert.False(owner.IsDisposed); - owner.Dispose(); - Assert.True(owner.IsDisposed); + CustomMemoryForTest<int> manager; + using (manager = new CustomMemoryForTest<int>(a)) + { + Assert.False(manager.IsDisposed); + } + Assert.True(manager.IsDisposed); } - + [Fact] - public static void OwnedMemoryPinEmptyArray() + public static void MemoryManagerPinEmptyArray() { - int[] a = {}; - OwnedMemory<int> owner = new CustomMemoryForTest<int>(a); - MemoryHandle handle = owner.Pin(); - Assert.True(handle.HasPointer); + int[] a = { }; + MemoryManager<int> manager = new CustomMemoryForTest<int>(a); + MemoryHandle handle = manager.Pin(); + unsafe + { + Assert.True(handle.Pointer != null); + } } [Fact] - public static void OwnedMemoryPinArray() + public static void MemoryManagerPinArray() { int[] array = { 1, 2, 3, 4, 5 }; - OwnedMemory<int> owner = new CustomMemoryForTest<int>(array); - MemoryHandle handle = owner.Pin(); - Assert.True(handle.HasPointer); + MemoryManager<int> manager = new CustomMemoryForTest<int>(array); + MemoryHandle handle = manager.Pin(); unsafe { int* pointer = (int*)handle.Pointer; + Assert.True(pointer != null); GC.Collect(); - for (int i = 0; i < owner.Memory.Length; i++) + for (int i = 0; i < manager.Memory.Length; i++) { Assert.Equal(array[i], pointer[i]); } @@ -120,7 +125,7 @@ namespace System.MemoryTests [Fact] [OuterLoop] [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Desktop framework doesn't support large arrays by default.")] - public static void OwnedMemoryPinLargeArray() + public static void MemoryManagerPinLargeArray() { // Early-out: we can only run this test on 64-bit platforms. if (IntPtr.Size == 4) @@ -129,38 +134,20 @@ namespace System.MemoryTests } int[] array = new int[0x2000_0000]; // will produce array with total byte length > 2 GB - OwnedMemory<int> owner = new CustomMemoryForTest<int>(array); - Assert.Throws<ArgumentOutOfRangeException>(() => owner.Pin(int.MinValue)); + MemoryManager<int> manager = new CustomMemoryForTest<int>(array); + Assert.Throws<ArgumentOutOfRangeException>(() => manager.Pin(int.MinValue)); } [Fact] - public static void MemoryFromOwnedMemoryAfterDispose() + public static void SpanFromMemoryManagerAfterDispose() { int[] a = { 91, 92, -93, 94 }; - OwnedMemory<int> owner = new CustomMemoryForTest<int>(a); - owner.Dispose(); - Assert.Throws<ObjectDisposedException>(() => owner.Memory); - } - - [Fact] - public static void DisposeOwnedMemoryAfterRetain() - { - int[] a = { 91, 92, -93, 94 }; - OwnedMemory<int> owner = new CustomMemoryForTest<int>(a); - owner.Retain(); - Assert.Throws<InvalidOperationException>(() => owner.Dispose()); - owner.Release(); - } + MemoryManager<int> manager; + using (manager = new CustomMemoryForTest<int>(a)) + { - [Fact] - public static void DisposeOwnedMemoryAfterRetainAndRelease() - { - int[] a = { 91, 92, -93, 94 }; - OwnedMemory<int> owner = new CustomMemoryForTest<int>(a); - owner.Retain(); - owner.Release(); - owner.Dispose(); - Assert.True(owner.IsDisposed); + } + Assert.Throws<ObjectDisposedException>(() => manager.GetSpan()); } } diff --git a/src/System.Memory/tests/Memory/Pin.cs b/src/System.Memory/tests/Memory/Pin.cs index 2d0d8ff523..a52af07006 100644 --- a/src/System.Memory/tests/Memory/Pin.cs +++ b/src/System.Memory/tests/Memory/Pin.cs @@ -15,10 +15,10 @@ namespace System.MemoryTests int[] array = { 1, 2, 3, 4, 5 }; Memory<int> memory = array; MemoryHandle handle = memory.Pin(); - Assert.True(handle.HasPointer); unsafe { int* pointer = (int*)handle.Pointer; + Assert.True(pointer != null); GC.Collect(); @@ -35,7 +35,10 @@ namespace System.MemoryTests { Memory<int> memory = new int[0]; MemoryHandle handle = memory.Pin(); - Assert.True(handle.HasPointer); + unsafe + { + Assert.True(handle.Pointer != null); + } handle.Dispose(); } @@ -44,7 +47,6 @@ namespace System.MemoryTests { Memory<int> memory = default; MemoryHandle handle = memory.Pin(); - Assert.False(handle.HasPointer); unsafe { Assert.True(handle.Pointer == null); @@ -60,10 +62,10 @@ namespace System.MemoryTests memory = memory.Slice(1); MemoryHandle handle = memory.Pin(); Span<int> span = memory.Span; - Assert.True(handle.HasPointer); unsafe { int* pointer = (int*)handle.Pointer; + Assert.True(pointer != null); GC.Collect(); @@ -81,16 +83,16 @@ namespace System.MemoryTests } [Fact] - public static void OwnedMemoryPin() + public static void MemoryManagerPin() { int[] array = { 1, 2, 3, 4, 5 }; - OwnedMemory<int> owner = new CustomMemoryForTest<int>(array); - Memory<int> memory = owner.Memory; + MemoryManager<int> manager = new CustomMemoryForTest<int>(array); + Memory<int> memory = manager.Memory; MemoryHandle handle = memory.Pin(); - Assert.True(handle.HasPointer); unsafe { int* pointer = (int*)handle.Pointer; + Assert.True(pointer != null); GC.Collect(); @@ -103,17 +105,17 @@ namespace System.MemoryTests } [Fact] - public static void OwnedMemoryPinAndSlice() + public static void MemoryManagerPinAndSlice() { int[] array = { 1, 2, 3, 4, 5 }; - OwnedMemory<int> owner = new CustomMemoryForTest<int>(array); - Memory<int> memory = owner.Memory.Slice(1); + MemoryManager<int> manager = new CustomMemoryForTest<int>(array); + Memory<int> memory = manager.Memory.Slice(1); MemoryHandle handle = memory.Pin(); Span<int> span = memory.Span; - Assert.True(handle.HasPointer); unsafe { int* pointer = (int*)handle.Pointer; + Assert.True(pointer != null); GC.Collect(); diff --git a/src/System.Memory/tests/Memory/Retain.cs b/src/System.Memory/tests/Memory/Retain.cs deleted file mode 100644 index 1ef319103c..0000000000 --- a/src/System.Memory/tests/Memory/Retain.cs +++ /dev/null @@ -1,165 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Buffers; -using Xunit; - -namespace System.MemoryTests -{ - public static partial class MemoryTests - { - - [Fact] - public static void MemoryRetainWithoutPinning() - { - int[] array = { 1, 2, 3, 4, 5 }; - Memory<int> memory = array; - MemoryHandle handle = memory.Retain(); - Assert.False(handle.HasPointer); - unsafe - { - Assert.True(handle.Pointer == null); - } - handle.Dispose(); - } - - [Fact] - public static void MemoryRetainWithPinning() - { - int[] array = { 1, 2, 3, 4, 5 }; - Memory<int> memory = array; - MemoryHandle handle = memory.Retain(pin: true); - Assert.True(handle.HasPointer); - unsafe - { - int* pointer = (int*)handle.Pointer; - - GC.Collect(); - - for (int i = 0; i < memory.Length; i++) - { - Assert.Equal(array[i], pointer[i]); - } - } - handle.Dispose(); - } - - [Fact] - public static void MemoryFromEmptyArrayRetainWithPinning() - { - Memory<int> memory = new int[0]; - MemoryHandle handle = memory.Retain(pin: true); - Assert.True(handle.HasPointer); - handle.Dispose(); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public static void DefaultMemoryRetain(bool pin) - { - Memory<int> memory = default; - MemoryHandle handle = memory.Retain(pin: pin); - Assert.False(handle.HasPointer); - unsafe - { - Assert.True(handle.Pointer == null); - } - handle.Dispose(); - } - - [Fact] - public static void MemoryRetainWithPinningAndSlice() - { - int[] array = { 1, 2, 3, 4, 5 }; - Memory<int> memory = array; - memory = memory.Slice(1); - MemoryHandle handle = memory.Retain(pin: true); - Span<int> span = memory.Span; - Assert.True(handle.HasPointer); - unsafe - { - int* pointer = (int*)handle.Pointer; - - GC.Collect(); - - for (int i = 0; i < memory.Length; i++) - { - Assert.Equal(array[i + 1], pointer[i]); - } - - for (int i = 0; i < memory.Length; i++) - { - Assert.Equal(array[i + 1], span[i]); - } - } - handle.Dispose(); - } - - [Fact] - public static void OwnedMemoryRetainWithoutPinning() - { - int[] array = { 1, 2, 3, 4, 5 }; - OwnedMemory<int> owner = new CustomMemoryForTest<int>(array); - Memory<int> memory = owner.Memory; - MemoryHandle handle = memory.Retain(); - Assert.False(handle.HasPointer); - unsafe - { - Assert.True(handle.Pointer == null); - } - handle.Dispose(); - } - - [Fact] - public static void OwnedMemoryRetainWithPinning() - { - int[] array = { 1, 2, 3, 4, 5 }; - OwnedMemory<int> owner = new CustomMemoryForTest<int>(array); - Memory<int> memory = owner.Memory; - MemoryHandle handle = memory.Retain(pin: true); - Assert.True(handle.HasPointer); - unsafe - { - int* pointer = (int*)handle.Pointer; - - GC.Collect(); - - for (int i = 0; i < memory.Length; i++) - { - Assert.Equal(array[i], pointer[i]); - } - } - handle.Dispose(); - } - - [Fact] - public static void OwnedMemoryRetainWithPinningAndSlice() - { - int[] array = { 1, 2, 3, 4, 5 }; - OwnedMemory<int> owner = new CustomMemoryForTest<int>(array); - Memory<int> memory = owner.Memory.Slice(1); - MemoryHandle handle = memory.Retain(pin: true); - Span<int> span = memory.Span; - Assert.True(handle.HasPointer); - unsafe - { - int* pointer = (int*)handle.Pointer; - - GC.Collect(); - - for (int i = 0; i < memory.Length; i++) - { - Assert.Equal(array[i + 1], pointer[i]); - } - - for (int i = 0; i < memory.Length; i++) - { - Assert.Equal(array[i + 1], span[i]); - } - } - handle.Dispose(); - } - } -} diff --git a/src/System.Memory/tests/Memory/Slice.cs b/src/System.Memory/tests/Memory/Slice.cs index c047472175..16e330cf3b 100644 --- a/src/System.Memory/tests/Memory/Slice.cs +++ b/src/System.Memory/tests/Memory/Slice.cs @@ -19,11 +19,11 @@ namespace System.MemoryTests Assert.Equal(4, memory.Length); Assert.True(Unsafe.AreSame(ref a[6], ref MemoryMarshal.GetReference(memory.Span))); - OwnedMemory<int> owner = new CustomMemoryForTest<int>(a); - Memory<int> memoryFromOwner = owner.Memory.Slice(6); + MemoryManager<int> manager = new CustomMemoryForTest<int>(a); + Memory<int> memoryFromManager = manager.Memory.Slice(6); - Assert.Equal(4, memoryFromOwner.Length); - Assert.True(Unsafe.AreSame(ref a[6], ref MemoryMarshal.GetReference(memoryFromOwner.Span))); + Assert.Equal(4, memoryFromManager.Length); + Assert.True(Unsafe.AreSame(ref a[6], ref MemoryMarshal.GetReference(memoryFromManager.Span))); } [Fact] @@ -34,11 +34,11 @@ namespace System.MemoryTests Assert.Equal(0, memory.Length); Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref MemoryMarshal.GetReference(memory.Span), 1))); - OwnedMemory<int> owner = new CustomMemoryForTest<int>(a); - Memory<int> memoryFromOwner = owner.Memory.Slice(a.Length); + MemoryManager<int> manager = new CustomMemoryForTest<int>(a); + Memory<int> memoryFromManager = manager.Memory.Slice(a.Length); - Assert.Equal(0, memoryFromOwner.Length); - Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref MemoryMarshal.GetReference(memoryFromOwner.Span), 1))); + Assert.Equal(0, memoryFromManager.Length); + Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref MemoryMarshal.GetReference(memoryFromManager.Span), 1))); } [Fact] @@ -49,11 +49,11 @@ namespace System.MemoryTests Assert.Equal(5, memory.Length); Assert.True(Unsafe.AreSame(ref a[3], ref MemoryMarshal.GetReference(memory.Span))); - OwnedMemory<int> owner = new CustomMemoryForTest<int>(a); - Memory<int> memoryFromOwner = owner.Memory.Slice(3, 5); + MemoryManager<int> manager = new CustomMemoryForTest<int>(a); + Memory<int> memoryFromManager = manager.Memory.Slice(3, 5); - Assert.Equal(5, memoryFromOwner.Length); - Assert.True(Unsafe.AreSame(ref a[3], ref MemoryMarshal.GetReference(memoryFromOwner.Span))); + Assert.Equal(5, memoryFromManager.Length); + Assert.True(Unsafe.AreSame(ref a[3], ref MemoryMarshal.GetReference(memoryFromManager.Span))); } [Fact] @@ -64,11 +64,11 @@ namespace System.MemoryTests Assert.Equal(6, memory.Length); Assert.True(Unsafe.AreSame(ref a[4], ref MemoryMarshal.GetReference(memory.Span))); - OwnedMemory<int> owner = new CustomMemoryForTest<int>(a); - Memory<int> memoryFromOwner = owner.Memory.Slice(4, 6); + MemoryManager<int> manager = new CustomMemoryForTest<int>(a); + Memory<int> memoryFromManager = manager.Memory.Slice(4, 6); - Assert.Equal(6, memoryFromOwner.Length); - Assert.True(Unsafe.AreSame(ref a[4], ref MemoryMarshal.GetReference(memoryFromOwner.Span))); + Assert.Equal(6, memoryFromManager.Length); + Assert.True(Unsafe.AreSame(ref a[4], ref MemoryMarshal.GetReference(memoryFromManager.Span))); } [Fact] @@ -79,11 +79,11 @@ namespace System.MemoryTests Assert.Equal(0, memory.Length); Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref MemoryMarshal.GetReference(memory.Span), 1))); - OwnedMemory<int> owner = new CustomMemoryForTest<int>(a); - Memory<int> memoryFromOwner = owner.Memory.Slice(a.Length, 0); + MemoryManager<int> manager = new CustomMemoryForTest<int>(a); + Memory<int> memoryFromManager = manager.Memory.Slice(a.Length, 0); - Assert.Equal(0, memoryFromOwner.Length); - Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref MemoryMarshal.GetReference(memoryFromOwner.Span), 1))); + Assert.Equal(0, memoryFromManager.Length); + Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref MemoryMarshal.GetReference(memoryFromManager.Span), 1))); } [Fact] @@ -98,8 +98,8 @@ namespace System.MemoryTests Assert.Throws<ArgumentOutOfRangeException>(() => new Memory<int>(a).Slice(a.Length + 1, 0)); Assert.Throws<ArgumentOutOfRangeException>(() => new Memory<int>(a).Slice(a.Length, 1)); - OwnedMemory<int> owner = new CustomMemoryForTest<int>(a); - Memory<int> memory = owner.Memory; + MemoryManager<int> manager = new CustomMemoryForTest<int>(a); + Memory<int> memory = manager.Memory; Assert.Throws<ArgumentOutOfRangeException>(() => memory.Slice(-1)); Assert.Throws<ArgumentOutOfRangeException>(() => memory.Slice(a.Length + 1)); diff --git a/src/System.Memory/tests/Memory/Span.cs b/src/System.Memory/tests/Memory/Span.cs index 607a56a82e..472310d786 100644 --- a/src/System.Memory/tests/Memory/Span.cs +++ b/src/System.Memory/tests/Memory/Span.cs @@ -21,8 +21,8 @@ namespace System.MemoryTests memory = new Memory<int>(a, 0, a.Length); memory.Span.Validate(91, 92, -93, 94); - OwnedMemory<int> owner = new CustomMemoryForTest<int>(a); - owner.Memory.Span.Validate(91, 92, -93, 94); + MemoryManager<int> manager = new CustomMemoryForTest<int>(a); + manager.Memory.Span.Validate(91, 92, -93, 94); } [Fact] @@ -37,8 +37,8 @@ namespace System.MemoryTests memory = new Memory<long>(a, 0, a.Length); memory.Span.Validate(91, -92, 93, 94, -95); - OwnedMemory<long> owner = new CustomMemoryForTest<long>(a); - owner.Memory.Span.Validate(91, -92, 93, 94, -95); + MemoryManager<long> manager = new CustomMemoryForTest<long>(a); + manager.Memory.Span.Validate(91, -92, 93, 94, -95); } [Fact] @@ -55,8 +55,8 @@ namespace System.MemoryTests memory = new Memory<object>(a, 0, a.Length); memory.Span.ValidateReferenceType(o1, o2); - OwnedMemory<object> owner = new CustomMemoryForTest<object>(a); - owner.Memory.Span.ValidateReferenceType(o1, o2); + MemoryManager<object> manager = new CustomMemoryForTest<object>(a); + manager.Memory.Span.ValidateReferenceType(o1, o2); } [Fact] @@ -71,8 +71,8 @@ namespace System.MemoryTests memory = new Memory<int>(empty, 0, empty.Length); memory.Span.ValidateNonNullEmpty(); - OwnedMemory<int> owner = new CustomMemoryForTest<int>(empty); - owner.Memory.Span.Validate(); + MemoryManager<int> manager = new CustomMemoryForTest<int>(empty); + manager.Memory.Span.Validate(); } [Fact] @@ -90,8 +90,8 @@ namespace System.MemoryTests memory = new Memory<int>(aAsIntArray, 0, aAsIntArray.Length); memory.Span.Validate(42, -1); - OwnedMemory<int> owner = new CustomMemoryForTest<int>(aAsIntArray); - owner.Memory.Span.Validate(42, -1); + MemoryManager<int> manager = new CustomMemoryForTest<int>(aAsIntArray); + manager.Memory.Span.Validate(42, -1); } [Fact] diff --git a/src/System.Memory/tests/Memory/ToString.cs b/src/System.Memory/tests/Memory/ToString.cs index e6eb713c8a..98be45eee6 100644 --- a/src/System.Memory/tests/Memory/ToString.cs +++ b/src/System.Memory/tests/Memory/ToString.cs @@ -69,28 +69,28 @@ namespace System.MemoryTests } [Fact] - public static void ToStringFromMemoryFromOwnedMemory() + public static void ToStringFromMemoryFromMemoryManager() { int[] a = { 91, 92, -93, 94 }; - OwnedMemory<int> intOwner = new CustomMemoryForTest<int>(a); - Assert.Equal("System.Memory<Int32>[4]", intOwner.Memory.ToString()); + MemoryManager<int> intManager = new CustomMemoryForTest<int>(a); + Assert.Equal("System.Memory<Int32>[4]", intManager.Memory.ToString()); - intOwner = new CustomMemoryForTest<int>(Array.Empty<int>()); - Assert.Equal("System.Memory<Int32>[0]", intOwner.Memory.ToString()); + intManager = new CustomMemoryForTest<int>(Array.Empty<int>()); + Assert.Equal("System.Memory<Int32>[0]", intManager.Memory.ToString()); char[] charArray = { '1', '2', '-', '4' }; - OwnedMemory<char> charOwner = new CustomMemoryForTest<char>(charArray); - Assert.Equal("12-4", charOwner.Memory.ToString()); + MemoryManager<char> charManager = new CustomMemoryForTest<char>(charArray); + Assert.Equal("12-4", charManager.Memory.ToString()); - charOwner = new CustomMemoryForTest<char>(Array.Empty<char>()); - Assert.Equal("", charOwner.Memory.ToString()); + charManager = new CustomMemoryForTest<char>(Array.Empty<char>()); + Assert.Equal("", charManager.Memory.ToString()); string[] strArray = { "91", "92", "-93", "94" }; - OwnedMemory<string> strOwner = new CustomMemoryForTest<string>(strArray); - Assert.Equal("System.Memory<String>[4]", strOwner.Memory.ToString()); + MemoryManager<string> strManager = new CustomMemoryForTest<string>(strArray); + Assert.Equal("System.Memory<String>[4]", strManager.Memory.ToString()); - strOwner = new CustomMemoryForTest<string>(Array.Empty<string>()); - Assert.Equal("System.Memory<String>[0]", strOwner.Memory.ToString()); + strManager = new CustomMemoryForTest<string>(Array.Empty<string>()); + Assert.Equal("System.Memory<String>[0]", strManager.Memory.ToString()); } [Fact] diff --git a/src/System.Memory/tests/MemoryMarshal/TryGetArray.cs b/src/System.Memory/tests/MemoryMarshal/TryGetArray.cs index 0443ed7a6e..045d5e9a7c 100644 --- a/src/System.Memory/tests/MemoryMarshal/TryGetArray.cs +++ b/src/System.Memory/tests/MemoryMarshal/TryGetArray.cs @@ -33,11 +33,11 @@ namespace System.MemoryTests } [Fact] - public static void OwnedMemoryTryGetArray() + public static void MemoryManagerTryGetArray() { int[] array = new int[10]; - OwnedMemory<int> owner = new CustomMemoryForTest<int>(array); - ReadOnlyMemory<int> memory = owner.Memory; + MemoryManager<int> manager = new CustomMemoryForTest<int>(array); + ReadOnlyMemory<int> memory = manager.Memory; Assert.True(MemoryMarshal.TryGetArray(memory, out ArraySegment<int> segment)); Assert.Equal(array.Length, segment.Count); @@ -62,22 +62,22 @@ namespace System.MemoryTests } [Fact] - public static void TryGetArrayFromEmptyOwnedMemory() + public static void TryGetArrayFromEmptyMemoryManager() { int[] array = new int[0]; - OwnedMemory<int> owner = new CustomMemoryForTest<int>(array); + MemoryManager<int> manager = new CustomMemoryForTest<int>(array); - Assert.True(MemoryMarshal.TryGetArray(owner.Memory, out ArraySegment<int> segment)); + Assert.True(MemoryMarshal.TryGetArray(manager.Memory, out ArraySegment<int> segment)); Assert.Same(array, segment.Array); Assert.Equal(0, segment.Array.Length); } [Fact] - public static void TryGetArrayFromEmptyNonRetrievableOwnedMemory() + public static void TryGetArrayFromEmptyNonRetrievableMemoryManager() { - using (var owner = new NativeOwnedMemory(0)) + using (var manager = new NativeMemoryManager(0)) { - Assert.True(MemoryMarshal.TryGetArray(owner.Memory, out ArraySegment<byte> segment)); + Assert.True(MemoryMarshal.TryGetArray(manager.Memory, out ArraySegment<byte> segment)); Assert.Equal(0, segment.Array.Length); } } diff --git a/src/System.Memory/tests/MemoryMarshal/TryGetMemoryManager.cs b/src/System.Memory/tests/MemoryMarshal/TryGetMemoryManager.cs new file mode 100644 index 0000000000..3935af40a1 --- /dev/null +++ b/src/System.Memory/tests/MemoryMarshal/TryGetMemoryManager.cs @@ -0,0 +1,89 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers; +using System.Runtime.InteropServices; +using Xunit; + +namespace System.MemoryTests +{ + public static partial class MemoryMarshalTests + { + [Fact] + public static void TryGetMemoryManagerFromDefaultMemory() + { + ReadOnlyMemory<int> memory = default; + Assert.False(MemoryMarshal.TryGetMemoryManager(memory, out MemoryManager<int> manager)); + Assert.Null(manager); + } + + [Fact] + public static void TryGetMemoryManager() + { + int[] array = new int[10]; + MemoryManager<int> originalManager = new CustomMemoryForTest<int>(array); + ReadOnlyMemory<int> memory = originalManager.Memory; + + Assert.True(MemoryMarshal.TryGetMemoryManager(memory, out CustomMemoryForTest<int> customManager)); + Assert.Same(originalManager, customManager); + + Assert.True(MemoryMarshal.TryGetMemoryManager(memory, out MemoryManager<int> manager)); + Assert.Same(originalManager, manager); + + Assert.False(MemoryMarshal.TryGetMemoryManager(memory, out OtherMemoryForTest<int> notManager)); + Assert.Null(notManager); + } + + [Fact] + public static void TryGetMemoryManagerFromDefaultMemory_IndexLength() + { + ReadOnlyMemory<int> memory = default; + Assert.False(MemoryMarshal.TryGetMemoryManager(memory, out MemoryManager<int> manager, out int index, out int length)); + Assert.Equal(0, index); + Assert.Equal(0, length); + Assert.Null(manager); + } + + [Fact] + public static void TryGetMemoryManager_IndexLength() + { + int[] array = new int[10]; + MemoryManager<int> originalManager = new CustomMemoryForTest<int>(array); + ReadOnlyMemory<int> memory = originalManager.Memory; + + for (int i = 0; i < array.Length; i++) + { + Assert.True(MemoryMarshal.TryGetMemoryManager(memory.Slice(i), out CustomMemoryForTest<int> customOwner, out int index, out int length)); + Assert.Same(originalManager, customOwner); + Assert.Equal(i, index); + Assert.Equal(array.Length - i, length); + } + + for (int i = 0; i < array.Length; i++) + { + Assert.True(MemoryMarshal.TryGetMemoryManager(memory.Slice(i), out MemoryManager<int> manager, out int index, out int length)); + Assert.Same(originalManager, manager); + Assert.Equal(i, index); + Assert.Equal(array.Length - i, length); + } + + for (int i = 0; i < array.Length; i++) + { + Assert.False(MemoryMarshal.TryGetMemoryManager(memory.Slice(i), out OtherMemoryForTest<int> notManager, out int index, out int length)); + Assert.Null(notManager); + } + } + + internal class OtherMemoryForTest<T> : MemoryManager<T> + { + public OtherMemoryForTest() { } + + public override int Length => 0; + public override Span<T> GetSpan() => throw new NotImplementedException(); + public override MemoryHandle Pin(int elementIndex = 0) => throw new NotImplementedException(); + protected override void Dispose(bool disposing) => throw new NotImplementedException(); + public override void Unpin() => throw new NotImplementedException(); + } + } +} diff --git a/src/System.Memory/tests/MemoryMarshal/TryGetOwnedMemory.cs b/src/System.Memory/tests/MemoryMarshal/TryGetOwnedMemory.cs deleted file mode 100644 index c786042f02..0000000000 --- a/src/System.Memory/tests/MemoryMarshal/TryGetOwnedMemory.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Buffers; -using System.Runtime.InteropServices; -using Xunit; - -namespace System.MemoryTests -{ - public static partial class MemoryMarshalTests - { - [Fact] - public static void TryGetOwnedMemoryFromDefaultMemory() - { - ReadOnlyMemory<int> memory = default; - Assert.False(MemoryMarshal.TryGetOwnedMemory(memory, out OwnedMemory<int> owner)); - Assert.Null(owner); - } - - [Fact] - public static void TryGetOwnedMemory() - { - int[] array = new int[10]; - OwnedMemory<int> orignalOwner = new CustomMemoryForTest<int>(array); - ReadOnlyMemory<int> memory = orignalOwner.Memory; - - Assert.True(MemoryMarshal.TryGetOwnedMemory(memory, out CustomMemoryForTest<int> customOwner)); - Assert.Same(orignalOwner, customOwner); - - Assert.True(MemoryMarshal.TryGetOwnedMemory(memory, out OwnedMemory<int> owner)); - Assert.Same(orignalOwner, owner); - - Assert.False(MemoryMarshal.TryGetOwnedMemory(memory, out OtherMemoryForTest<int> notOwner)); - Assert.Null(notOwner); - } - - [Fact] - public static void TryGetOwnedMemoryFromDefaultMemory_IndexLength() - { - ReadOnlyMemory<int> memory = default; - Assert.False(MemoryMarshal.TryGetOwnedMemory(memory, out OwnedMemory<int> owner, out int index, out int length)); - Assert.Equal(0, index); - Assert.Equal(0, length); - Assert.Null(owner); - } - - [Fact] - public static void TryGetOwnedMemory_IndexLength() - { - int[] array = new int[10]; - OwnedMemory<int> orignalOwner = new CustomMemoryForTest<int>(array); - ReadOnlyMemory<int> memory = orignalOwner.Memory; - - for (int i = 0; i < array.Length; i++) - { - Assert.True(MemoryMarshal.TryGetOwnedMemory(memory.Slice(i), out CustomMemoryForTest<int> customOwner, out int index, out int length)); - Assert.Same(orignalOwner, customOwner); - Assert.Equal(i, index); - Assert.Equal(array.Length - i, length); - } - - for (int i = 0; i < array.Length; i++) - { - Assert.True(MemoryMarshal.TryGetOwnedMemory(memory.Slice(i), out OwnedMemory<int> owner, out int index, out int length)); - Assert.Same(orignalOwner, owner); - Assert.Equal(i, index); - Assert.Equal(array.Length - i, length); - } - - for (int i = 0; i < array.Length; i++) - { - Assert.False(MemoryMarshal.TryGetOwnedMemory(memory.Slice(i), out OtherMemoryForTest<int> notOwner, out int index, out int length)); - Assert.Null(notOwner); - } - } - - internal class OtherMemoryForTest<T> : OwnedMemory<T> - { - public OtherMemoryForTest() { } - - public override int Length => 0; - public override bool IsDisposed => false; - protected override bool IsRetained => false; - public override Span<T> Span => throw new NotImplementedException(); - public override MemoryHandle Pin(int byteOffset = 0) => throw new NotImplementedException(); - protected override bool TryGetArray(out ArraySegment<T> segment) => throw new NotImplementedException(); - protected override void Dispose(bool disposing) => throw new NotImplementedException(); - public override void Retain() => throw new NotImplementedException(); - public override bool Release() => throw new NotImplementedException(); - } - } -} diff --git a/src/System.Memory/tests/MemoryPool/MemoryPool.cs b/src/System.Memory/tests/MemoryPool/MemoryPool.cs index 63d3419972..7af3434e70 100644 --- a/src/System.Memory/tests/MemoryPool/MemoryPool.cs +++ b/src/System.Memory/tests/MemoryPool/MemoryPool.cs @@ -26,10 +26,9 @@ namespace System.MemoryTests MemoryPool<int> mp = MemoryPool<int>.Shared; mp.Dispose(); mp.Dispose(); - using (OwnedMemory<int> block = mp.Rent(10)) + using (IMemoryOwner<int> block = mp.Rent(10)) { - Assert.True(block.Length >= 10); - block.Release(); + Assert.True(block.Memory.Length >= 10); } } @@ -44,11 +43,13 @@ namespace System.MemoryTests public static void MemoryPoolSpan() { MemoryPool<int> pool = MemoryPool<int>.Shared; - using (OwnedMemory<int> block = pool.Rent(10)) + using (IMemoryOwner<int> block = pool.Rent(10)) { - Span<int> sp = block.Span; - Assert.Equal(block.Length, sp.Length); - using (MemoryHandle newMemoryHandle = block.Pin()) + Memory<int> memory = block.Memory; + Span<int> sp = memory.Span; + Assert.Equal(memory.Length, sp.Length); + Assert.True(MemoryMarshal.TryGetMemoryManager<int, MemoryManager<int>>(memory, out MemoryManager<int> manager)); + using (MemoryHandle newMemoryHandle = manager.Pin()) { unsafe { @@ -56,69 +57,63 @@ namespace System.MemoryTests Assert.Equal((IntPtr)newMemoryHandle.Pointer, (IntPtr)pSpan); } } - block.Release(); } } [Theory] [InlineData(0)] [InlineData(3)] - [InlineData(10 * sizeof(int))] - public static void MemoryPoolPin(int byteOffset) + [InlineData(10)] + public static void MemoryPoolPin(int elementIndex) { MemoryPool<int> pool = MemoryPool<int>.Shared; - using (OwnedMemory<int> block = pool.Rent(10)) + using (IMemoryOwner<int> block = pool.Rent(10)) { - Span<int> sp = block.Span; - Assert.Equal(block.Length, sp.Length); - using (MemoryHandle newMemoryHandle = block.Pin(byteOffset: byteOffset)) + Memory<int> memory = block.Memory; + Span<int> sp = memory.Span; + Assert.Equal(memory.Length, sp.Length); + Assert.True(MemoryMarshal.TryGetMemoryManager<int, MemoryManager<int>>(memory, out MemoryManager<int> manager)); + using (MemoryHandle newMemoryHandle = manager.Pin(elementIndex: elementIndex)) { unsafe { - void* pSpan = Unsafe.AsPointer(ref MemoryMarshal.GetReference(sp)); - Assert.Equal((IntPtr)pSpan, ((IntPtr)newMemoryHandle.Pointer) - byteOffset); + void* pSpan = Unsafe.AsPointer(ref MemoryMarshal.GetReference(sp.Slice(elementIndex))); + Assert.Equal((IntPtr)pSpan, ((IntPtr)newMemoryHandle.Pointer)); } } - block.Release(); } } [Theory] [InlineData(-1)] [InlineData(int.MinValue)] - public static void MemoryPoolPinBadOffset(int byteOffset) + public static void MemoryPoolPinBadOffset(int elementIndex) { MemoryPool<int> pool = MemoryPool<int>.Shared; - OwnedMemory<int> block = pool.Rent(10); - Span<int> sp = block.Span; - Assert.Equal(block.Length, sp.Length); - Assert.Throws<ArgumentOutOfRangeException>(() => block.Pin(byteOffset: byteOffset)); + IMemoryOwner<int> block = pool.Rent(10); + Memory<int> memory = block.Memory; + Span<int> sp = memory.Span; + Assert.Equal(memory.Length, sp.Length); + Assert.Throws<ArgumentOutOfRangeException>(() => ((MemoryManager<int>)block).Pin(elementIndex: elementIndex)); } [Fact] public static void MemoryPoolPinOffsetAtEnd() { MemoryPool<int> pool = MemoryPool<int>.Shared; - OwnedMemory<int> block = pool.Rent(10); - Span<int> sp = block.Span; - Assert.Equal(block.Length, sp.Length); - - int byteOffset = 0; - try - { - byteOffset = checked(block.Length * sizeof(int)); - } - catch (OverflowException) - { - return; // The pool gave us a very large block - too big to compute the byteOffset needed to carry out this test. Skip. - } + IMemoryOwner<int> block = pool.Rent(10); + Memory<int> memory = block.Memory; + Span<int> sp = memory.Span; + Assert.Equal(memory.Length, sp.Length); - using (MemoryHandle newMemoryHandle = block.Pin(byteOffset: byteOffset)) + int elementIndex = memory.Length; + Assert.True(MemoryMarshal.TryGetMemoryManager<int, MemoryManager<int>>(memory, out MemoryManager<int> manager)); + using (MemoryHandle newMemoryHandle = manager.Pin(elementIndex: elementIndex)) { unsafe { - void* pSpan = Unsafe.AsPointer(ref MemoryMarshal.GetReference(sp)); - Assert.Equal((IntPtr)pSpan, ((IntPtr)newMemoryHandle.Pointer) - byteOffset); + void* pSpan = Unsafe.AsPointer(ref MemoryMarshal.GetReference(sp.Slice(elementIndex))); + Assert.Equal((IntPtr)pSpan, ((IntPtr)newMemoryHandle.Pointer)); } } } @@ -127,28 +122,21 @@ namespace System.MemoryTests public static void MemoryPoolPinBadOffsetTooLarge() { MemoryPool<int> pool = MemoryPool<int>.Shared; - OwnedMemory<int> block = pool.Rent(10); - Span<int> sp = block.Span; - Assert.Equal(block.Length, sp.Length); - - int byteOffset = 0; - try - { - byteOffset = checked(block.Length * sizeof(int) + 1); - } - catch (OverflowException) - { - return; // The pool gave us a very large block - too big to compute the byteOffset needed to carry out this test. Skip. - } + IMemoryOwner<int> block = pool.Rent(10); + Memory<int> memory = block.Memory; + Span<int> sp = memory.Span; + Assert.Equal(memory.Length, sp.Length); - Assert.Throws<ArgumentOutOfRangeException>(() => block.Pin(byteOffset: byteOffset)); + int elementIndex = memory.Length + 1; + Assert.True(MemoryMarshal.TryGetMemoryManager<int, MemoryManager<int>>(memory, out MemoryManager<int> manager)); + Assert.Throws<ArgumentOutOfRangeException>(() => manager.Pin(elementIndex: elementIndex)); } [Fact] public static void EachRentalIsUniqueUntilDisposed() { MemoryPool<int> pool = MemoryPool<int>.Shared; - List<OwnedMemory<int>> priorBlocks = new List<OwnedMemory<int>>(); + List<IMemoryOwner<int>> priorBlocks = new List<IMemoryOwner<int>>(); Random r = new Random(42); List<int> testInputs = new List<int>(); @@ -159,14 +147,16 @@ namespace System.MemoryTests foreach (int minBufferSize in testInputs) { - OwnedMemory<int> newBlock = pool.Rent(minBufferSize); - Assert.True(newBlock.Length >= minBufferSize); - - foreach (OwnedMemory<int> prior in priorBlocks) + IMemoryOwner<int> newBlock = pool.Rent(minBufferSize); + Memory<int> memory = newBlock.Memory; + Assert.True(memory.Length >= minBufferSize); + Assert.True(MemoryMarshal.TryGetMemoryManager<int, MemoryManager<int>>(newBlock.Memory, out MemoryManager<int> newManager)); + foreach (IMemoryOwner<int> prior in priorBlocks) { - using (MemoryHandle priorMemoryHandle = prior.Pin()) + Assert.True(MemoryMarshal.TryGetMemoryManager<int, MemoryManager<int>>(prior.Memory, out MemoryManager<int> priorManager)); + using (MemoryHandle priorMemoryHandle = priorManager.Pin()) { - using (MemoryHandle newMemoryHandle = newBlock.Pin()) + using (MemoryHandle newMemoryHandle = newManager.Pin()) { unsafe { @@ -178,9 +168,10 @@ namespace System.MemoryTests priorBlocks.Add(newBlock); } - foreach (OwnedMemory<int> prior in priorBlocks) + foreach (IMemoryOwner<int> prior in priorBlocks) { - prior.Release(); + Assert.True(MemoryMarshal.TryGetMemoryManager<int, MemoryManager<int>>(prior.Memory, out MemoryManager<int> priorManager)); + priorManager.Unpin(); prior.Dispose(); } } @@ -188,10 +179,15 @@ namespace System.MemoryTests [Fact] public static void RentWithDefaultSize() { - using (OwnedMemory<int> block = MemoryPool<int>.Shared.Rent(minBufferSize: -1)) + using (IMemoryOwner<int> block = MemoryPool<int>.Shared.Rent(minBufferSize: -1)) + { + Assert.True(block.Memory.Length >= 1); + } + + using (IMemoryOwner<int> block = MemoryPool<int>.Shared.Rent(minBufferSize: -1)) { - Assert.True(block.Length >= 1); - block.Release(); + Assert.True(block.Memory.Length >= 1); + block.Dispose(); // intentional double dispose } } @@ -215,66 +211,29 @@ namespace System.MemoryTests [Fact] public static void MemoryPoolTryGetArray() { - using (OwnedMemory<int> block = MemoryPool<int>.Shared.Rent(42)) + using (IMemoryOwner<int> block = MemoryPool<int>.Shared.Rent(42)) { Memory<int> memory = block.Memory; bool success = MemoryMarshal.TryGetArray(memory, out ArraySegment<int> arraySegment); Assert.True(success); - Assert.Equal(block.Length, arraySegment.Count); + Assert.Equal(memory.Length, arraySegment.Count); unsafe { - void* pSpan = Unsafe.AsPointer(ref MemoryMarshal.GetReference(block.Span)); + Assert.True(MemoryMarshal.TryGetMemoryManager<int, MemoryManager<int>>(memory, out MemoryManager<int> manager)); + void* pSpan = Unsafe.AsPointer(ref MemoryMarshal.GetReference(manager.GetSpan())); fixed (int* pArray = arraySegment.Array) { Assert.Equal((IntPtr)pSpan, (IntPtr)pArray); } } - block.Release(); - } - } - - [Fact] - public static void RefCounting() - { - using (OwnedMemory<int> block = MemoryPool<int>.Shared.Rent(42)) - { - block.Retain(); - block.Retain(); - block.Retain(); - - bool moreToGo; - moreToGo = block.Release(); - Assert.True(moreToGo); - - moreToGo = block.Release(); - Assert.True(moreToGo); - - moreToGo = block.Release(); - Assert.True(moreToGo); - - moreToGo = block.Release(); - Assert.False(moreToGo); - - Assert.Throws<ObjectDisposedException>(() => block.Release()); } } [Fact] - public static void IsDisposed() - { - OwnedMemory<int> block = MemoryPool<int>.Shared.Rent(42); - Assert.False(block.IsDisposed); - block.Release(); - Assert.True(block.IsDisposed); - block.Dispose(); - Assert.True(block.IsDisposed); - } - - [Fact] public static void ExtraDisposesAreIgnored() { - OwnedMemory<int> block = MemoryPool<int>.Shared.Rent(42); - block.Release(); + IMemoryOwner<int> block = MemoryPool<int>.Shared.Rent(42); + ((MemoryManager<int>)block).Unpin(); block.Dispose(); block.Dispose(); } @@ -282,64 +241,39 @@ namespace System.MemoryTests [Fact] public static void NoSpanAfterDispose() { - OwnedMemory<int> block = MemoryPool<int>.Shared.Rent(42); - block.Release(); - block.Dispose(); - Assert.Throws<ObjectDisposedException>(() => block.Span.DontBox()); - } - - [Fact] - public static void NoRetainAfterDispose() - { - OwnedMemory<int> block = MemoryPool<int>.Shared.Rent(42); - block.Release(); + IMemoryOwner<int> block = MemoryPool<int>.Shared.Rent(42); + ((MemoryManager<int>)block).Unpin(); block.Dispose(); - Assert.Throws<ObjectDisposedException>(() => block.Retain()); + Assert.Throws<ObjectDisposedException>(() => ((MemoryManager<int>)block).GetSpan().DontBox()); } [Fact] - public static void NoRelease_AfterDispose() + public static void NoPinAfterDispose() { - OwnedMemory<int> block = MemoryPool<int>.Shared.Rent(42); - block.Release(); + IMemoryOwner<int> block = MemoryPool<int>.Shared.Rent(42); + ((MemoryManager<int>)block).Unpin(); block.Dispose(); - Assert.Throws<ObjectDisposedException>(() => block.Release()); + Assert.Throws<ObjectDisposedException>(() => ((MemoryManager<int>)block).Pin()); } [Fact] - public static void NoPinAfterDispose() + public static void NoUnpin_AfterDispose() { - OwnedMemory<int> block = MemoryPool<int>.Shared.Rent(42); - block.Release(); + IMemoryOwner<int> block = MemoryPool<int>.Shared.Rent(42); + ((MemoryManager<int>)block).Unpin(); block.Dispose(); - Assert.Throws<ObjectDisposedException>(() => block.Pin()); + Assert.Throws<ObjectDisposedException>(() => ((MemoryManager<int>)block).Unpin()); } [Fact] public static void NoTryGetArrayAfterDispose() { - OwnedMemory<int> block = MemoryPool<int>.Shared.Rent(42); + IMemoryOwner<int> block = MemoryPool<int>.Shared.Rent(42); Memory<int> memory = block.Memory; - block.Release(); + ((MemoryManager<int>)block).Unpin(); block.Dispose(); Assert.Throws<ObjectDisposedException>(() => MemoryMarshal.TryGetArray(memory, out ArraySegment<int> arraySegment)); } - - [Fact] - public static void IsRetainedWhenReturned() - { - OwnedMemory<int> block = MemoryPool<int>.Shared.Rent(42); - Assert.False(block.Release()); - } - - [Fact] - public static void IsDisposedWhenReleased() - { - OwnedMemory<int> block = MemoryPool<int>.Shared.Rent(42); - block.Release(); - - Assert.True(block.IsDisposed); - } } } diff --git a/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceFactory.byte.cs b/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceFactory.byte.cs index d8734b34ec..fc5c05dac1 100644 --- a/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceFactory.byte.cs +++ b/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceFactory.byte.cs @@ -13,7 +13,6 @@ namespace System.Memory.Tests { public static ReadOnlySequenceFactoryByte ArrayFactory { get; } = new ArrayTestSequenceFactoryByte(); public static ReadOnlySequenceFactoryByte MemoryFactory { get; } = new MemoryTestSequenceFactoryByte(); - public static ReadOnlySequenceFactoryByte OwnedMemoryFactory { get; } = new OwnedMemoryTestSequenceFactoryByte(); public static ReadOnlySequenceFactoryByte SingleSegmentFactory { get; } = new SingleSegmentTestSequenceFactoryByte(); public static ReadOnlySequenceFactoryByte SegmentPerByteFactory { get; } = new BytePerSegmentTestSequenceFactoryByte(); @@ -55,21 +54,6 @@ namespace System.Memory.Tests } } - internal class OwnedMemoryTestSequenceFactoryByte : ReadOnlySequenceFactoryByte - { - public override ReadOnlySequence<byte> CreateOfSize(int size) - { - return CreateWithContent(new byte[size]); - } - - public override ReadOnlySequence<byte> CreateWithContent(byte[] data) - { - var startSegment = new byte[data.Length + 20]; - Array.Copy(data, 0, startSegment, 10, data.Length); - return new ReadOnlySequence<byte>(new CustomMemoryForTest<byte>(startSegment, 10, data.Length)); - } - } - internal class SingleSegmentTestSequenceFactoryByte : ReadOnlySequenceFactoryByte { public override ReadOnlySequence<byte> CreateOfSize(int size) diff --git a/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceFactory.char.cs b/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceFactory.char.cs index 273f5670bb..e23f1e43d2 100644 --- a/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceFactory.char.cs +++ b/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceFactory.char.cs @@ -15,7 +15,6 @@ namespace System.Memory.Tests public static ReadOnlySequenceFactoryChar ArrayFactory { get; } = new ArrayTestSequenceFactoryChar(); public static ReadOnlySequenceFactoryChar MemoryFactory { get; } = new MemoryTestSequenceFactoryChar(); public static ReadOnlySequenceFactoryChar StringFactory { get; } = new StringTestSequenceFactoryChar(); - public static ReadOnlySequenceFactoryChar OwnedMemoryFactory { get; } = new OwnedMemoryTestSequenceFactoryChar(); public static ReadOnlySequenceFactoryChar SingleSegmentFactory { get; } = new SingleSegmentTestSequenceFactoryChar(); public static ReadOnlySequenceFactoryChar SegmentPerCharFactory { get; } = new CharPerSegmentTestSequenceFactoryChar(); @@ -89,21 +88,6 @@ namespace System.Memory.Tests } } - internal class OwnedMemoryTestSequenceFactoryChar : ReadOnlySequenceFactoryChar - { - public override ReadOnlySequence<char> CreateOfSize(int size) - { - return CreateWithContent(new char[size]); - } - - public override ReadOnlySequence<char> CreateWithContent(char[] data) - { - var startSegment = new char[data.Length + 20]; - Array.Copy(data, 0, startSegment, 10, data.Length); - return new ReadOnlySequence<char>(new CustomMemoryForTest<char>(startSegment, 10, data.Length)); - } - } - internal class SingleSegmentTestSequenceFactoryChar : ReadOnlySequenceFactoryChar { public override ReadOnlySequence<char> CreateOfSize(int size) diff --git a/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.Common.cs b/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.Common.cs index c7971c4a33..6bc16ec8cf 100644 --- a/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.Common.cs +++ b/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.Common.cs @@ -264,7 +264,7 @@ namespace System.Memory.Tests } Assert.Equal(1, sizes.Count); - Assert.Equal(new [] { 100 }, sizes); + Assert.Equal(new[] { 100 }, sizes); } [Fact] @@ -282,7 +282,7 @@ namespace System.Memory.Tests } Assert.Equal(2, sizes.Count); - Assert.Equal(new [] { 100, 0 }, sizes); + Assert.Equal(new[] { 100, 0 }, sizes); } [Fact] @@ -300,7 +300,7 @@ namespace System.Memory.Tests } Assert.Equal(2, sizes.Count); - Assert.Equal(new [] { 100, 0 }, sizes); + Assert.Equal(new[] { 100, 0 }, sizes); } [Fact] @@ -396,33 +396,6 @@ namespace System.Memory.Tests } [Fact] - public void Ctor_OwnedMemory_Offset() - { - var ownedMemory = new CustomMemoryForTest<byte>(new byte[] { 1, 2, 3, 4, 5 }, 0, 5); - var buffer = new ReadOnlySequence<byte>(ownedMemory, 2, 3); - Assert.Equal(buffer.ToArray(), new byte[] { 3, 4, 5 }); - } - - [Fact] - public void Ctor_OwnedMemory_NoOffset() - { - var ownedMemory = new CustomMemoryForTest<byte>(new byte[] { 1, 2, 3, 4, 5 }, 0, 5); - var buffer = new ReadOnlySequence<byte>(ownedMemory); - Assert.Equal(buffer.ToArray(), new byte[] { 1, 2, 3, 4, 5 }); - } - - [Fact] - public void Ctor_OwnedMemory_ValidatesArguments() - { - var ownedMemory = new CustomMemoryForTest<byte>(new byte[] { 1, 2, 3, 4, 5 }, 0, 5); - Assert.Throws<ArgumentOutOfRangeException>(() => new ReadOnlySequence<byte>(ownedMemory, 6, 0)); - Assert.Throws<ArgumentOutOfRangeException>(() => new ReadOnlySequence<byte>(ownedMemory, 4, 2)); - Assert.Throws<ArgumentOutOfRangeException>(() => new ReadOnlySequence<byte>(ownedMemory, -4, 0)); - Assert.Throws<ArgumentOutOfRangeException>(() => new ReadOnlySequence<byte>(ownedMemory, 4, -2)); - Assert.Throws<ArgumentNullException>(() => new ReadOnlySequence<byte>((CustomMemoryForTest<byte>)null, 4, 2)); - } - - [Fact] public void Ctor_Memory() { var memory = new ReadOnlyMemory<byte>(new byte[] { 1, 2, 3, 4, 5 }); diff --git a/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.TryGet.cs b/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.TryGet.cs index b99a4f0fe6..9bde78b0ee 100644 --- a/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.TryGet.cs +++ b/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.TryGet.cs @@ -21,7 +21,7 @@ namespace System.Memory.Tests Assert.Equal(3, array.Count); Assert.False(SequenceMarshal.TryGetReadOnlySequenceSegment(buffer, out _, out _, out _, out _)); - Assert.False(SequenceMarshal.TryGetOwnedMemory(buffer, out _, out _, out _)); + Assert.False(SequenceMarshal.TryGetMemoryManager(buffer, out _, out _, out _)); // Array can be retrieved with TryGetReadOnlyMemory Assert.True(SequenceMarshal.TryGetReadOnlyMemory(buffer, out ReadOnlyMemory<byte> newMemory)); @@ -38,7 +38,7 @@ namespace System.Memory.Tests Assert.Equal(new byte[] { 3, 4, 5 }, newMemory.ToArray()); Assert.False(SequenceMarshal.TryGetReadOnlySequenceSegment(buffer, out ReadOnlySequenceSegment<byte> startSegment, out int startIndex, out ReadOnlySequenceSegment<byte> endSegment, out int endIndex)); - Assert.False(SequenceMarshal.TryGetOwnedMemory(buffer, out _, out _, out _)); + Assert.False(SequenceMarshal.TryGetMemoryManager(buffer, out _, out _, out _)); // Memory is internally decomposed to its container so it would be accessible via TryGetArray Assert.True(SequenceMarshal.TryGetArray(buffer, out ArraySegment<byte> array)); @@ -58,30 +58,11 @@ namespace System.Memory.Tests Assert.Equal(text.Substring(2, 3).ToCharArray(), newMemory.ToArray()); Assert.False(SequenceMarshal.TryGetReadOnlySequenceSegment(buffer, out ReadOnlySequenceSegment<char> startSegment, out int startIndex, out ReadOnlySequenceSegment<char> endSegment, out int endIndex)); - Assert.False(SequenceMarshal.TryGetOwnedMemory(buffer, out _, out _, out _)); + Assert.False(SequenceMarshal.TryGetMemoryManager(buffer, out _, out _, out _)); Assert.False(SequenceMarshal.TryGetArray(buffer, out _)); } [Fact] - public void Ctor_OwnedMemory_Offset() - { - var ownedMemory = new CustomMemoryForTest<byte>(new byte[] { 1, 2, 3, 4, 5 }, 0, 5); - var buffer = new ReadOnlySequence<byte>(ownedMemory, 2, 3); - - Assert.True(SequenceMarshal.TryGetOwnedMemory(buffer, out OwnedMemory<byte> newOwnedMemory, out int start, out int length)); - Assert.Equal(ownedMemory, newOwnedMemory); - Assert.Equal(2, start); - Assert.Equal(3, length); - - Assert.False(SequenceMarshal.TryGetReadOnlySequenceSegment(buffer, out _, out _, out _, out _)); - Assert.False(SequenceMarshal.TryGetArray(buffer, out _)); - - // OwnedMemory can be retrieved with TryGetReadOnlyMemory - Assert.True(SequenceMarshal.TryGetReadOnlyMemory(buffer, out ReadOnlyMemory<byte> newMemory)); - Assert.Equal(new byte[] { 3, 4, 5 }, newMemory.ToArray()); - } - - [Fact] public void Ctor_IMemoryList_SingleBlock() { var memoryListSegment = new BufferSegment<byte>(new byte[] { 1, 2, 3, 4, 5 }); @@ -96,7 +77,7 @@ namespace System.Memory.Tests Assert.Equal(5, endIndex); Assert.False(SequenceMarshal.TryGetArray(buffer, out _)); - Assert.False(SequenceMarshal.TryGetOwnedMemory(buffer, out _, out _, out _)); + Assert.False(SequenceMarshal.TryGetMemoryManager(buffer, out _, out _, out _)); // Single block can be retrieved with TryGetReadOnlyMemory Assert.True(SequenceMarshal.TryGetReadOnlyMemory(buffer, out ReadOnlyMemory<byte> newMemory)); @@ -119,7 +100,7 @@ namespace System.Memory.Tests Assert.Equal(1, endIndex); Assert.False(SequenceMarshal.TryGetArray(buffer, out _)); - Assert.False(SequenceMarshal.TryGetOwnedMemory(buffer, out _, out _, out _)); + Assert.False(SequenceMarshal.TryGetMemoryManager(buffer, out _, out _, out _)); // Multi-block can't be retrieved with TryGetReadOnlyMemory Assert.False(SequenceMarshal.TryGetReadOnlyMemory(buffer, out _)); diff --git a/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.byte.cs b/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.byte.cs index 3e49ca7ac6..42a48d8e9c 100644 --- a/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.byte.cs +++ b/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.byte.cs @@ -16,11 +16,6 @@ namespace System.Memory.Tests public Array() : base(ReadOnlySequenceFactoryByte.ArrayFactory) { } } - public class OwnedMemory : ReadOnlySequenceTestsByte - { - public OwnedMemory() : base(ReadOnlySequenceFactoryByte.OwnedMemoryFactory) { } - } - public class Memory : ReadOnlySequenceTestsByte { public Memory() : base(ReadOnlySequenceFactoryByte.MemoryFactory) { } diff --git a/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.char.cs b/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.char.cs index 53a2f1a1a4..9b025fbbe8 100644 --- a/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.char.cs +++ b/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.char.cs @@ -21,11 +21,6 @@ namespace System.Memory.Tests public String() : base(ReadOnlySequenceFactoryChar.StringFactory) { } } - public class OwnedMemory : ReadOnlySequenceTestsChar - { - public OwnedMemory() : base(ReadOnlySequenceFactoryChar.OwnedMemoryFactory) { } - } - public class Memory : ReadOnlySequenceTestsChar { public Memory() : base(ReadOnlySequenceFactoryChar.MemoryFactory) { } diff --git a/src/System.Memory/tests/ReadOnlyMemory/Pin.cs b/src/System.Memory/tests/ReadOnlyMemory/Pin.cs index 803612035c..0fb69de3e5 100644 --- a/src/System.Memory/tests/ReadOnlyMemory/Pin.cs +++ b/src/System.Memory/tests/ReadOnlyMemory/Pin.cs @@ -15,10 +15,10 @@ namespace System.MemoryTests int[] array = { 1, 2, 3, 4, 5 }; ReadOnlyMemory<int> memory = array; MemoryHandle handle = memory.Pin(); - Assert.True(handle.HasPointer); unsafe { int* pointer = (int*)handle.Pointer; + Assert.True(pointer != null); GC.Collect(); @@ -35,7 +35,10 @@ namespace System.MemoryTests { ReadOnlyMemory<int> memory = new int[0]; MemoryHandle handle = memory.Pin(); - Assert.True(handle.HasPointer); + unsafe + { + Assert.True(handle.Pointer != null); + } handle.Dispose(); } @@ -44,7 +47,6 @@ namespace System.MemoryTests { ReadOnlyMemory<int> memory = default; MemoryHandle handle = memory.Pin(); - Assert.False(handle.HasPointer); unsafe { Assert.True(handle.Pointer == null); @@ -60,10 +62,10 @@ namespace System.MemoryTests memory = memory.Slice(1); MemoryHandle handle = memory.Pin(); ReadOnlySpan<int> span = memory.Span; - Assert.True(handle.HasPointer); unsafe { int* pointer = (int*)handle.Pointer; + Assert.True(pointer != null); GC.Collect(); @@ -81,16 +83,16 @@ namespace System.MemoryTests } [Fact] - public static void OwnedMemoryPin() + public static void MemoryManagerPin() { int[] array = { 1, 2, 3, 4, 5 }; - OwnedMemory<int> owner = new CustomMemoryForTest<int>(array); - ReadOnlyMemory<int> memory = owner.Memory; + MemoryManager<int> manager = new CustomMemoryForTest<int>(array); + ReadOnlyMemory<int> memory = manager.Memory; MemoryHandle handle = memory.Pin(); - Assert.True(handle.HasPointer); unsafe { int* pointer = (int*)handle.Pointer; + Assert.True(pointer != null); GC.Collect(); @@ -103,17 +105,17 @@ namespace System.MemoryTests } [Fact] - public static void OwnedMemoryPinAndSlice() + public static void MemoryManagerPinAndSlice() { int[] array = { 1, 2, 3, 4, 5 }; - OwnedMemory<int> owner = new CustomMemoryForTest<int>(array); - ReadOnlyMemory<int> memory = owner.Memory.Slice(1); + MemoryManager<int> manager = new CustomMemoryForTest<int>(array); + ReadOnlyMemory<int> memory = manager.Memory.Slice(1); MemoryHandle handle = memory.Pin(); ReadOnlySpan<int> span = memory.Span; - Assert.True(handle.HasPointer); unsafe { int* pointer = (int*)handle.Pointer; + Assert.True(pointer != null); GC.Collect(); diff --git a/src/System.Memory/tests/ReadOnlyMemory/Retain.cs b/src/System.Memory/tests/ReadOnlyMemory/Retain.cs deleted file mode 100644 index d438c78d77..0000000000 --- a/src/System.Memory/tests/ReadOnlyMemory/Retain.cs +++ /dev/null @@ -1,165 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Buffers; -using Xunit; - -namespace System.MemoryTests -{ - public static partial class ReadOnlyMemoryTests - { - - [Fact] - public static void MemoryRetainWithoutPinning() - { - int[] array = { 1, 2, 3, 4, 5 }; - ReadOnlyMemory<int> memory = array; - MemoryHandle handle = memory.Retain(); - Assert.False(handle.HasPointer); - unsafe - { - Assert.True(handle.Pointer == null); - } - handle.Dispose(); - } - - [Fact] - public static void MemoryRetainWithPinning() - { - int[] array = { 1, 2, 3, 4, 5 }; - ReadOnlyMemory<int> memory = array; - MemoryHandle handle = memory.Retain(pin: true); - Assert.True(handle.HasPointer); - unsafe - { - int* pointer = (int*)handle.Pointer; - - GC.Collect(); - - for (int i = 0; i < memory.Length; i++) - { - Assert.Equal(array[i], pointer[i]); - } - } - handle.Dispose(); - } - - [Fact] - public static void MemoryFromEmptyArrayRetainWithPinning() - { - ReadOnlyMemory<int> memory = new int[0]; - MemoryHandle handle = memory.Retain(pin: true); - Assert.True(handle.HasPointer); - handle.Dispose(); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public static void DefaultMemoryRetain(bool pin) - { - ReadOnlyMemory<int> memory = default; - MemoryHandle handle = memory.Retain(pin: pin); - Assert.False(handle.HasPointer); - unsafe - { - Assert.True(handle.Pointer == null); - } - handle.Dispose(); - } - - [Fact] - public static void MemoryRetainWithPinningAndSlice() - { - int[] array = { 1, 2, 3, 4, 5 }; - ReadOnlyMemory<int> memory = array; - memory = memory.Slice(1); - MemoryHandle handle = memory.Retain(pin: true); - ReadOnlySpan<int> span = memory.Span; - Assert.True(handle.HasPointer); - unsafe - { - int* pointer = (int*)handle.Pointer; - - GC.Collect(); - - for (int i = 0; i < memory.Length; i++) - { - Assert.Equal(array[i + 1], pointer[i]); - } - - for (int i = 0; i < memory.Length; i++) - { - Assert.Equal(array[i + 1], span[i]); - } - } - handle.Dispose(); - } - - [Fact] - public static void OwnedMemoryRetainWithoutPinning() - { - int[] array = { 1, 2, 3, 4, 5 }; - OwnedMemory<int> owner = new CustomMemoryForTest<int>(array); - ReadOnlyMemory<int> memory = owner.Memory; - MemoryHandle handle = memory.Retain(); - Assert.False(handle.HasPointer); - unsafe - { - Assert.True(handle.Pointer == null); - } - handle.Dispose(); - } - - [Fact] - public static void OwnedMemoryRetainWithPinning() - { - int[] array = { 1, 2, 3, 4, 5 }; - OwnedMemory<int> owner = new CustomMemoryForTest<int>(array); - ReadOnlyMemory<int> memory = owner.Memory; - MemoryHandle handle = memory.Retain(pin: true); - Assert.True(handle.HasPointer); - unsafe - { - int* pointer = (int*)handle.Pointer; - - GC.Collect(); - - for (int i = 0; i < memory.Length; i++) - { - Assert.Equal(array[i], pointer[i]); - } - } - handle.Dispose(); - } - - [Fact] - public static void OwnedMemoryRetainWithPinningAndSlice() - { - int[] array = { 1, 2, 3, 4, 5 }; - OwnedMemory<int> owner = new CustomMemoryForTest<int>(array); - ReadOnlyMemory<int> memory = owner.Memory.Slice(1); - MemoryHandle handle = memory.Retain(pin: true); - ReadOnlySpan<int> span = memory.Span; - Assert.True(handle.HasPointer); - unsafe - { - int* pointer = (int*)handle.Pointer; - - GC.Collect(); - - for (int i = 0; i < memory.Length; i++) - { - Assert.Equal(array[i + 1], pointer[i]); - } - - for (int i = 0; i < memory.Length; i++) - { - Assert.Equal(array[i + 1], span[i]); - } - } - handle.Dispose(); - } - } -} diff --git a/src/System.Memory/tests/ReadOnlyMemory/Slice.cs b/src/System.Memory/tests/ReadOnlyMemory/Slice.cs index 70404ecca4..dbeb91d62e 100644 --- a/src/System.Memory/tests/ReadOnlyMemory/Slice.cs +++ b/src/System.Memory/tests/ReadOnlyMemory/Slice.cs @@ -19,11 +19,11 @@ namespace System.MemoryTests Assert.Equal(4, memory.Length); Assert.True(Unsafe.AreSame(ref a[6], ref Unsafe.AsRef(in MemoryMarshal.GetReference(memory.Span)))); - OwnedMemory<int> owner = new CustomMemoryForTest<int>(a); - ReadOnlyMemory<int> memoryFromOwner = ((ReadOnlyMemory<int>)owner.Memory).Slice(6); + MemoryManager<int> manager = new CustomMemoryForTest<int>(a); + ReadOnlyMemory<int> memoryFromManager = ((ReadOnlyMemory<int>)manager.Memory).Slice(6); - Assert.Equal(4, memoryFromOwner.Length); - Assert.True(Unsafe.AreSame(ref a[6], ref Unsafe.AsRef(in MemoryMarshal.GetReference(memoryFromOwner.Span)))); + Assert.Equal(4, memoryFromManager.Length); + Assert.True(Unsafe.AreSame(ref a[6], ref Unsafe.AsRef(in MemoryMarshal.GetReference(memoryFromManager.Span)))); } [Fact] @@ -34,11 +34,11 @@ namespace System.MemoryTests Assert.Equal(0, memory.Length); Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref Unsafe.AsRef(in MemoryMarshal.GetReference(memory.Span)), 1))); - OwnedMemory<int> owner = new CustomMemoryForTest<int>(a); - ReadOnlyMemory<int> memoryFromOwner = ((ReadOnlyMemory<int>)owner.Memory).Slice(a.Length); + MemoryManager<int> manager = new CustomMemoryForTest<int>(a); + ReadOnlyMemory<int> memoryFromManager = ((ReadOnlyMemory<int>)manager.Memory).Slice(a.Length); - Assert.Equal(0, memoryFromOwner.Length); - Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref Unsafe.AsRef(in MemoryMarshal.GetReference(memoryFromOwner.Span)), 1))); + Assert.Equal(0, memoryFromManager.Length); + Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref Unsafe.AsRef(in MemoryMarshal.GetReference(memoryFromManager.Span)), 1))); } [Fact] @@ -49,11 +49,11 @@ namespace System.MemoryTests Assert.Equal(5, memory.Length); Assert.True(Unsafe.AreSame(ref a[3], ref Unsafe.AsRef(in MemoryMarshal.GetReference(memory.Span)))); - OwnedMemory<int> owner = new CustomMemoryForTest<int>(a); - ReadOnlyMemory<int> memoryFromOwner = ((ReadOnlyMemory<int>)owner.Memory).Slice(3, 5); + MemoryManager<int> manager = new CustomMemoryForTest<int>(a); + ReadOnlyMemory<int> memoryFromManager = ((ReadOnlyMemory<int>)manager.Memory).Slice(3, 5); - Assert.Equal(5, memoryFromOwner.Length); - Assert.True(Unsafe.AreSame(ref a[3], ref Unsafe.AsRef(in MemoryMarshal.GetReference(memoryFromOwner.Span)))); + Assert.Equal(5, memoryFromManager.Length); + Assert.True(Unsafe.AreSame(ref a[3], ref Unsafe.AsRef(in MemoryMarshal.GetReference(memoryFromManager.Span)))); } [Fact] @@ -64,11 +64,11 @@ namespace System.MemoryTests Assert.Equal(6, memory.Length); Assert.True(Unsafe.AreSame(ref a[4], ref Unsafe.AsRef(in MemoryMarshal.GetReference(memory.Span)))); - OwnedMemory<int> owner = new CustomMemoryForTest<int>(a); - ReadOnlyMemory<int> memoryFromOwner = ((ReadOnlyMemory<int>)owner.Memory).Slice(4, 6); + MemoryManager<int> manager = new CustomMemoryForTest<int>(a); + ReadOnlyMemory<int> memoryFromManager = ((ReadOnlyMemory<int>)manager.Memory).Slice(4, 6); - Assert.Equal(6, memoryFromOwner.Length); - Assert.True(Unsafe.AreSame(ref a[4], ref Unsafe.AsRef(in MemoryMarshal.GetReference(memoryFromOwner.Span)))); + Assert.Equal(6, memoryFromManager.Length); + Assert.True(Unsafe.AreSame(ref a[4], ref Unsafe.AsRef(in MemoryMarshal.GetReference(memoryFromManager.Span)))); } [Fact] @@ -79,11 +79,11 @@ namespace System.MemoryTests Assert.Equal(0, memory.Length); Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref Unsafe.AsRef(in MemoryMarshal.GetReference(memory.Span)), 1))); - OwnedMemory<int> owner = new CustomMemoryForTest<int>(a); - ReadOnlyMemory<int> memoryFromOwner = ((ReadOnlyMemory<int>)owner.Memory).Slice(a.Length, 0); + MemoryManager<int> manager = new CustomMemoryForTest<int>(a); + ReadOnlyMemory<int> memoryFromManager = ((ReadOnlyMemory<int>)manager.Memory).Slice(a.Length, 0); - Assert.Equal(0, memoryFromOwner.Length); - Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref Unsafe.AsRef(in MemoryMarshal.GetReference(memoryFromOwner.Span)), 1))); + Assert.Equal(0, memoryFromManager.Length); + Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref Unsafe.AsRef(in MemoryMarshal.GetReference(memoryFromManager.Span)), 1))); } [Fact] @@ -98,8 +98,8 @@ namespace System.MemoryTests Assert.Throws<ArgumentOutOfRangeException>(() => new ReadOnlyMemory<int>(a).Slice(a.Length + 1, 0)); Assert.Throws<ArgumentOutOfRangeException>(() => new ReadOnlyMemory<int>(a).Slice(a.Length, 1)); - OwnedMemory<int> owner = new CustomMemoryForTest<int>(a); - ReadOnlyMemory<int> memory = owner.Memory; + MemoryManager<int> manager = new CustomMemoryForTest<int>(a); + ReadOnlyMemory<int> memory = manager.Memory; Assert.Throws<ArgumentOutOfRangeException>(() => memory.Slice(-1)); Assert.Throws<ArgumentOutOfRangeException>(() => memory.Slice(a.Length + 1)); diff --git a/src/System.Memory/tests/ReadOnlyMemory/Span.cs b/src/System.Memory/tests/ReadOnlyMemory/Span.cs index 65029dc03b..781d3f5a55 100644 --- a/src/System.Memory/tests/ReadOnlyMemory/Span.cs +++ b/src/System.Memory/tests/ReadOnlyMemory/Span.cs @@ -21,8 +21,8 @@ namespace System.MemoryTests memory = new ReadOnlyMemory<int>(a, 0, a.Length); memory.Span.Validate(91, 92, -93, 94); - OwnedMemory<int> owner = new CustomMemoryForTest<int>(a); - ((ReadOnlyMemory<int>)owner.Memory).Span.Validate(91, 92, -93, 94); + MemoryManager<int> manager = new CustomMemoryForTest<int>(a); + ((ReadOnlyMemory<int>)manager.Memory).Span.Validate(91, 92, -93, 94); } [Fact] @@ -37,8 +37,8 @@ namespace System.MemoryTests memory = new ReadOnlyMemory<long>(a, 0, a.Length); memory.Span.Validate(91, -92, 93, 94, -95); - OwnedMemory<long> owner = new CustomMemoryForTest<long>(a); - ((ReadOnlyMemory<long>)owner.Memory).Span.Validate(91, -92, 93, 94, -95); + MemoryManager<long> manager = new CustomMemoryForTest<long>(a); + ((ReadOnlyMemory<long>)manager.Memory).Span.Validate(91, -92, 93, 94, -95); } [Fact] @@ -55,8 +55,8 @@ namespace System.MemoryTests memory = new ReadOnlyMemory<object>(a, 0, a.Length); memory.Span.ValidateReferenceType(o1, o2); - OwnedMemory<object> owner = new CustomMemoryForTest<object>(a); - ((ReadOnlyMemory<object>)owner.Memory).Span.ValidateReferenceType(o1, o2); + MemoryManager<object> manager = new CustomMemoryForTest<object>(a); + ((ReadOnlyMemory<object>)manager.Memory).Span.ValidateReferenceType(o1, o2); } [Fact] @@ -71,8 +71,8 @@ namespace System.MemoryTests memory = new ReadOnlyMemory<int>(empty, 0, empty.Length); memory.Span.ValidateNonNullEmpty(); - OwnedMemory<int> owner = new CustomMemoryForTest<int>(empty); - ((ReadOnlyMemory<int>)owner.Memory).Span.Validate(); + MemoryManager<int> manager = new CustomMemoryForTest<int>(empty); + ((ReadOnlyMemory<int>)manager.Memory).Span.Validate(); } [Fact] @@ -90,8 +90,8 @@ namespace System.MemoryTests memory = new ReadOnlyMemory<int>(aAsIntArray, 0, aAsIntArray.Length); memory.Span.Validate(42, -1); - OwnedMemory<int> owner = new CustomMemoryForTest<int>(aAsIntArray); - ((ReadOnlyMemory<int>)owner.Memory).Span.Validate(42, -1); + MemoryManager<int> manager = new CustomMemoryForTest<int>(aAsIntArray); + ((ReadOnlyMemory<int>)manager.Memory).Span.Validate(42, -1); } [Fact] diff --git a/src/System.Memory/tests/ReadOnlyMemory/ToString.cs b/src/System.Memory/tests/ReadOnlyMemory/ToString.cs index 8781c53942..4ce511a219 100644 --- a/src/System.Memory/tests/ReadOnlyMemory/ToString.cs +++ b/src/System.Memory/tests/ReadOnlyMemory/ToString.cs @@ -68,28 +68,28 @@ namespace System.MemoryTests } [Fact] - public static void ToStringFromMemoryFromOwnedMemory() + public static void ToStringFromMemoryFromMemoryManager() { int[] a = { 91, 92, -93, 94 }; - OwnedMemory<int> intOwner = new CustomMemoryForTest<int>(a); - Assert.Equal("System.ReadOnlyMemory<Int32>[4]", ((ReadOnlyMemory<int>)intOwner.Memory).ToString()); + MemoryManager<int> intManager = new CustomMemoryForTest<int>(a); + Assert.Equal("System.ReadOnlyMemory<Int32>[4]", ((ReadOnlyMemory<int>)intManager.Memory).ToString()); - intOwner = new CustomMemoryForTest<int>(Array.Empty<int>()); - Assert.Equal("System.ReadOnlyMemory<Int32>[0]", ((ReadOnlyMemory<int>)intOwner.Memory).ToString()); + intManager = new CustomMemoryForTest<int>(Array.Empty<int>()); + Assert.Equal("System.ReadOnlyMemory<Int32>[0]", ((ReadOnlyMemory<int>)intManager.Memory).ToString()); char[] charArray = { '1', '2', '-', '4' }; - OwnedMemory<char> charOwner = new CustomMemoryForTest<char>(charArray); - Assert.Equal("12-4", ((ReadOnlyMemory<char>)charOwner.Memory).ToString()); + MemoryManager<char> charManager = new CustomMemoryForTest<char>(charArray); + Assert.Equal("12-4", ((ReadOnlyMemory<char>)charManager.Memory).ToString()); - charOwner = new CustomMemoryForTest<char>(Array.Empty<char>()); - Assert.Equal("", ((ReadOnlyMemory<char>)charOwner.Memory).ToString()); + charManager = new CustomMemoryForTest<char>(Array.Empty<char>()); + Assert.Equal("", ((ReadOnlyMemory<char>)charManager.Memory).ToString()); string[] strArray = { "91", "92", "-93", "94" }; - OwnedMemory<string> strOwner = new CustomMemoryForTest<string>(strArray); - Assert.Equal("System.ReadOnlyMemory<String>[4]", ((ReadOnlyMemory<string>)strOwner.Memory).ToString()); + MemoryManager<string> strManager = new CustomMemoryForTest<string>(strArray); + Assert.Equal("System.ReadOnlyMemory<String>[4]", ((ReadOnlyMemory<string>)strManager.Memory).ToString()); - strOwner = new CustomMemoryForTest<string>(Array.Empty<string>()); - Assert.Equal("System.ReadOnlyMemory<String>[0]", ((ReadOnlyMemory<string>)strOwner.Memory).ToString()); + strManager = new CustomMemoryForTest<string>(Array.Empty<string>()); + Assert.Equal("System.ReadOnlyMemory<String>[0]", ((ReadOnlyMemory<string>)strManager.Memory).ToString()); } [Fact] diff --git a/src/System.Memory/tests/Resources/System.Memory.Tests.rd.xml b/src/System.Memory/tests/Resources/System.Memory.Tests.rd.xml index 7931c600d4..6dd7780ad6 100644 --- a/src/System.Memory/tests/Resources/System.Memory.Tests.rd.xml +++ b/src/System.Memory/tests/Resources/System.Memory.Tests.rd.xml @@ -1,6 +1,6 @@ <Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata"> <Library> - <!-- Needed because OwnedMemory is reflection blocked and xunit tries to reflect on this concrete type during discovery --> + <!-- Needed because MemoryManager is reflection blocked and xunit tries to reflect on this concrete type during discovery --> <Type Name="System.MemoryTests.CustomMemoryForTest{T}" Dynamic="Excluded" Browse="Excluded" Serialize="Excluded" /> </Library> </Directives>
\ No newline at end of file diff --git a/src/System.Memory/tests/Span/Reflection.cs b/src/System.Memory/tests/Span/Reflection.cs new file mode 100644 index 0000000000..652e0f161e --- /dev/null +++ b/src/System.Memory/tests/Span/Reflection.cs @@ -0,0 +1,186 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; +using System.Buffers; +using System.Buffers.Binary; +using System.Reflection; +using System.Runtime.InteropServices; +using System.MemoryTests; + +namespace System.SpanTests +{ + public static partial class SpanTests + { + // Calling Span APIs via Reflection is not supported yet. + // These tests check that using reflection results in graceful failures. See https://github.com/dotnet/coreclr/issues/17296 + // These tests are only relevant for fast span. + + [Fact] + public static void MemoryExtensions_StaticReturningReadOnlySpan() + { + Type type = typeof(MemoryExtensions); + + MethodInfo method = type.GetMethod(nameof(MemoryExtensions.AsSpan), new Type[] { typeof(string) }); + Assert.Throws<NotSupportedException>(() => method.Invoke(null, new object[] { "Hello" })); + + method = type.GetMethod(nameof(MemoryExtensions.AsSpan), new Type[] { typeof(string), typeof(int), typeof(int) }); + Assert.Throws<NotSupportedException>(() => method.Invoke(null, new object[] { "Hello", 1, 1 })); + } + + [Fact] + public static void MemoryExtensions_StaticWithSpanArguments() + { + Type type = typeof(MemoryExtensions); + + MethodInfo method = type.GetMethod(nameof(MemoryExtensions.CompareTo)); + + int result = (int)method.Invoke(null, new object[] { default, default, StringComparison.Ordinal }); + Assert.Equal(0, result); + } + + [Fact] + public static void BinaryPrimitives_StaticWithSpanArgument() + { + Type type = typeof(BinaryPrimitives); + + MethodInfo method = type.GetMethod(nameof(BinaryPrimitives.ReadInt16LittleEndian)); + Assert.Throws<TargetInvocationException>(() => method.Invoke(null, new object[] { default })); + + method = type.GetMethod(nameof(BinaryPrimitives.TryReadInt16LittleEndian)); + bool result = (bool)method.Invoke(null, new object[] { default, null }); + Assert.False(result); + } + + [Fact] + public static void MemoryMarshal_GenericStaticReturningSpan() + { + Type type = typeof(MemoryMarshal); + + int value = 0; + ref int refInt = ref value; + + MethodInfo method = type.GetMethod(nameof(MemoryMarshal.CreateSpan)).MakeGenericMethod((refInt.GetType())); + Assert.Throws<NotSupportedException>(() => method.Invoke(null, new object[] { null, 0 })); + } + + [Fact] + public static void Span_Constructor() + { + Type type = typeof(Span<int>); + + ConstructorInfo ctor = type.GetConstructor(new Type[] { typeof(int[]) }); + Assert.Throws<TargetException>(() => ctor.Invoke(new object[] { new int[10] })); + + ctor = type.GetConstructor(new Type[] { typeof(int[]), typeof(int), typeof(int) }); + Assert.Throws<TargetException>(() => ctor.Invoke(new object[] { new int[10], 1, 1 })); + + ctor = type.GetConstructor(new Type[] { typeof(void*), typeof(int) }); + Assert.Throws<TargetException>(() => ctor.Invoke(new object[] { null, 1 })); + } + + [Fact] + public static void Span_Property() + { + Type type = typeof(Span<int>); + + PropertyInfo property = type.GetProperty(nameof(Span<int>.Empty)); + Assert.Throws<NotSupportedException>(() => property.GetValue(default)); + } + + [Fact] + public static void Span_StaticOperator() + { + Type type = typeof(Span<int>); + + MethodInfo method = type.GetMethod("op_Equality"); + Assert.Throws<NotSupportedException>(() => method.Invoke(null, new object[] { default, default })); + + method = type.GetMethod("op_Inequality"); + Assert.Throws<NotSupportedException>(() => method.Invoke(null, new object[] { default, default })); + } + + [Fact] + public static void Span_InstanceMethod() + { + Type type = typeof(Span<int>); + + MethodInfo method = type.GetMethod(nameof(Span<int>.CopyTo), new Type[] { typeof(Span<int>) }); + Assert.Throws<NotSupportedException>(() => method.Invoke(default, new object[] { default })); + } + + [Fact] + public static void ReadOnlySpan_Constructor() + { + Type type = typeof(ReadOnlySpan<int>); + + ConstructorInfo ctor = type.GetConstructor(new Type[] { typeof(int[]) }); + Assert.Throws<TargetException>(() => ctor.Invoke(new object[] { new int[10] })); + + ctor = type.GetConstructor(new Type[] { typeof(int[]), typeof(int), typeof(int) }); + Assert.Throws<TargetException>(() => ctor.Invoke(new object[] { new int[10], 1, 1 })); + + ctor = type.GetConstructor(new Type[] { typeof(void*), typeof(int) }); + Assert.Throws<TargetException>(() => ctor.Invoke(new object[] { null, 1 })); + } + + [Fact] + public static void ReadOnlySpan_Property() + { + Type type = typeof(ReadOnlySpan<int>); + + PropertyInfo property = type.GetProperty(nameof(ReadOnlySpan<int>.Empty)); + Assert.Throws<NotSupportedException>(() => property.GetValue(default)); + } + + [Fact] + public static void ReadOnlySpan_Operator() + { + Type type = typeof(ReadOnlySpan<int>); + + MethodInfo method = type.GetMethod("op_Equality"); + Assert.Throws<NotSupportedException>(() => method.Invoke(null, new object[] { default, default })); + + method = type.GetMethod("op_Inequality"); + Assert.Throws<NotSupportedException>(() => method.Invoke(null, new object[] { default, default })); + } + + [Fact] + public static void ReadOnlySpan_InstanceMethod() + { + Type type = typeof(ReadOnlySpan<int>); + + MethodInfo method = type.GetMethod(nameof(ReadOnlySpan<int>.CopyTo), new Type[] { typeof(Span<int>) }); + Assert.Throws<NotSupportedException>(() => method.Invoke(default, new object[] { default })); + } + + [Fact] + public static void Memory_PropertyReturningSpan() + { + Type type = typeof(Memory<int>); + + PropertyInfo property = type.GetProperty(nameof(Memory<int>.Span)); + Assert.Throws<NotSupportedException>(() => property.GetValue(null)); + } + + [Fact] + public static void ReadOnlyMemory_PropertyReturningReadOnlySpan() + { + Type type = typeof(ReadOnlyMemory<int>); + + PropertyInfo property = type.GetProperty(nameof(ReadOnlyMemory<int>.Span)); + Assert.Throws<NotSupportedException>(() => property.GetValue(null)); + } + + [Fact] + public static void MemoryManager_MethodReturningSpan() + { + Type type = typeof(MemoryManager<int>); + + MemoryManager<int> manager = new CustomMemoryForTest<int>(new int[10]); + MethodInfo method = type.GetMethod(nameof(MemoryManager<int>.GetSpan), BindingFlags.Public | BindingFlags.Instance); + Assert.Throws<NotSupportedException>(() => method.Invoke(manager, null)); + } + } +} diff --git a/src/System.Memory/tests/System.Memory.Tests.csproj b/src/System.Memory/tests/System.Memory.Tests.csproj index 27d689a92a..19c6e3377b 100644 --- a/src/System.Memory/tests/System.Memory.Tests.csproj +++ b/src/System.Memory/tests/System.Memory.Tests.csproj @@ -16,6 +16,7 @@ <!-- Tests specific to the fast span --> <Compile Include="MemoryMarshal\CreateSpan.cs" /> <Compile Include="MemoryMarshal\CreateReadOnlySpan.cs" /> + <Compile Include="Span\Reflection.cs" /> </ItemGroup> <ItemGroup> <Compile Include="AllocationHelper.cs" /> @@ -162,9 +163,8 @@ <Compile Include="Memory\Equality.cs" /> <Compile Include="Memory\GetHashCode.cs" /> <Compile Include="Memory\ImplicitConversion.cs" /> - <Compile Include="Memory\OwnedMemory.cs" /> + <Compile Include="Memory\MemoryManager.cs" /> <Compile Include="Memory\Pin.cs" /> - <Compile Include="Memory\Retain.cs" /> <Compile Include="Memory\Slice.cs" /> <Compile Include="Memory\Span.cs" /> <Compile Include="Memory\Strings.cs" /> @@ -179,7 +179,7 @@ <Compile Include="MemoryMarshal\CastSpan.cs" /> <Compile Include="MemoryMarshal\GetReference.cs" /> <Compile Include="MemoryMarshal\TryGetArray.cs" /> - <Compile Include="MemoryMarshal\TryGetOwnedMemory.cs" /> + <Compile Include="MemoryMarshal\TryGetMemoryManager.cs" /> <Compile Include="MemoryMarshal\TryGetString.cs" /> <Compile Include="MemoryMarshal\ToEnumerable.cs" /> </ItemGroup> @@ -195,7 +195,6 @@ <Compile Include="ReadOnlyMemory\GetHashCode.cs" /> <Compile Include="ReadOnlyMemory\ImplicitConversion.cs" /> <Compile Include="ReadOnlyMemory\Pin.cs" /> - <Compile Include="ReadOnlyMemory\Retain.cs" /> <Compile Include="ReadOnlyMemory\Slice.cs" /> <Compile Include="ReadOnlyMemory\Span.cs" /> <Compile Include="ReadOnlyMemory\Strings.cs" /> @@ -236,8 +235,8 @@ <Compile Include="Base64\Base64TestHelper.cs" /> </ItemGroup> <ItemGroup> - <Compile Include="$(CommonTestPath)\System\Buffers\NativeOwnedMemory.cs"> - <Link>Common\System\Buffers\NativeOwnedMemory.cs</Link> + <Compile Include="$(CommonTestPath)\System\Buffers\NativeMemoryManager.cs"> + <Link>Common\System\Buffers\NativeMemoryManager.cs</Link> </Compile> <Compile Include="$(CommonPath)\System\MutableDecimal.cs"> <Link>Common\System\MutableDecimal.cs</Link> diff --git a/src/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs b/src/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs index 65ae9c528a..b4e6458718 100644 --- a/src/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs +++ b/src/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs @@ -194,8 +194,6 @@ namespace System.Net.Http set { - SecurityProtocol.ThrowOnNotAllowed(value, allowNone: true); - CheckDisposedOrStarted(); _sslProtocols = value; } @@ -929,6 +927,18 @@ namespace System.Net.Http uint optionData = 0; SslProtocols sslProtocols = (_sslProtocols == SslProtocols.None) ? SecurityProtocol.DefaultSecurityProtocols : _sslProtocols; + +#pragma warning disable 0618 // SSL2/SSL3 are deprecated + if ((sslProtocols & SslProtocols.Ssl2) != 0) + { + optionData |= Interop.WinHttp.WINHTTP_FLAG_SECURE_PROTOCOL_SSL2; + } + + if ((sslProtocols & SslProtocols.Ssl3) != 0) + { + optionData |= Interop.WinHttp.WINHTTP_FLAG_SECURE_PROTOCOL_SSL3; + } +#pragma warning restore 0618 if ((sslProtocols & SslProtocols.Tls) != 0) { diff --git a/src/System.Net.Http.WinHttpHandler/tests/UnitTests/WinHttpHandlerTest.cs b/src/System.Net.Http.WinHttpHandler/tests/UnitTests/WinHttpHandlerTest.cs index 7b4eb0eb42..bc5d9f1fdf 100644 --- a/src/System.Net.Http.WinHttpHandler/tests/UnitTests/WinHttpHandlerTest.cs +++ b/src/System.Net.Http.WinHttpHandler/tests/UnitTests/WinHttpHandlerTest.cs @@ -436,15 +436,6 @@ namespace System.Net.Http.WinHttpHandlerUnitTests } [Theory] - [ClassData(typeof(SslProtocolSupport.UnsupportedSslProtocolsTestData))] - public void SslProtocols_SetUsingUnsupported_Throws(SslProtocols protocol) - { - var handler = new WinHttpHandler(); - - Assert.Throws<NotSupportedException>(() => { handler.SslProtocols = protocol; }); - } - - [Theory] [ClassData(typeof(SslProtocolSupport.SupportedSslProtocolsTestData))] public void SslProtocols_SetUsingSupported_Success(SslProtocols protocol) { @@ -460,33 +451,27 @@ namespace System.Net.Http.WinHttpHandlerUnitTests handler.SslProtocols = SslProtocols.None; } - [Fact] - public void SslProtocols_SetUsingInvalidEnum_Throws() - { - var handler = new WinHttpHandler(); - - Assert.Throws<NotSupportedException>(() => { handler.SslProtocols = (SslProtocols)4096; }); - } - - [Fact] - public void SslProtocols_SetUsingValidEnums_ExpectedWinHttpHandleSettings() + [Theory] + [InlineData( + SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12, + Interop.WinHttp.WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | + Interop.WinHttp.WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 | + Interop.WinHttp.WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2)] +#pragma warning disable 0618 + [InlineData( + SslProtocols.Ssl2 | SslProtocols.Ssl3, + Interop.WinHttp.WINHTTP_FLAG_SECURE_PROTOCOL_SSL2 | + Interop.WinHttp.WINHTTP_FLAG_SECURE_PROTOCOL_SSL3)] +#pragma warning restore 0618 + public void SslProtocols_SetUsingValidEnums_ExpectedWinHttpHandleSettings( + SslProtocols specified, uint expectedProtocols) { var handler = new WinHttpHandler(); SendRequestHelper.Send( handler, - delegate - { - handler.SslProtocols = - SslProtocols.Tls | - SslProtocols.Tls11 | - SslProtocols.Tls12; - }); + delegate { handler.SslProtocols = specified; }); - uint expectedProtocols = - Interop.WinHttp.WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | - Interop.WinHttp.WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 | - Interop.WinHttp.WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2; Assert.Equal(expectedProtocols, APICallHistory.WinHttpOptionSecureProtocols); } diff --git a/src/System.Net.Http/src/System.Net.Http.csproj b/src/System.Net.Http/src/System.Net.Http.csproj index 8b4f8e05f9..e4790711d9 100644 --- a/src/System.Net.Http/src/System.Net.Http.csproj +++ b/src/System.Net.Http/src/System.Net.Http.csproj @@ -152,6 +152,9 @@ <Compile Include="System\Net\Http\SocketsHttpHandler\SocketsHttpHandler.cs" /> <Compile Include="System\Net\Http\SocketsHttpHandler\RawConnectionStream.cs" /> <Compile Include="System\Net\Http\SocketsHttpHandler\RedirectHandler.cs" /> + <Compile Include="$(CommonPath)\System\Collections\Concurrent\ConcurrentQueue_Segment.cs"> + <Link>Common\System\Collections\Concurrent\ConcurrentQueue_Segment.cs</Link> + </Compile> <Compile Include="$(CommonPath)\System\Net\NTAuthentication.Common.cs"> <Link>Common\System\Net\NTAuthentication.Common.cs</Link> </Compile> diff --git a/src/System.Net.Http/src/System/Net/Http/CurlHandler/CurlHandler.SslProvider.Linux.cs b/src/System.Net.Http/src/System/Net/Http/CurlHandler/CurlHandler.SslProvider.Linux.cs index fc6f5b88c7..55e583e137 100644 --- a/src/System.Net.Http/src/System/Net/Http/CurlHandler/CurlHandler.SslProvider.Linux.cs +++ b/src/System.Net.Http/src/System/Net/Http/CurlHandler/CurlHandler.SslProvider.Linux.cs @@ -209,16 +209,21 @@ namespace System.Net.Http return; } - // We explicitly disallow choosing SSL2/3. Make sure they were filtered out. - Debug.Assert((protocols & ~SecurityProtocol.AllowedSecurityProtocols) == 0, - "Disallowed protocols should have been filtered out."); - // libcurl supports options for either enabling all of the TLS1.* protocols or enabling - // just one of them; it doesn't currently support enabling two of the three, e.g. you can't + // just one protocol; it doesn't currently support enabling two of the three, e.g. you can't // pick TLS1.1 and TLS1.2 but not TLS1.0, but you can select just TLS1.2. Interop.Http.CurlSslVersion curlSslVersion; switch (protocols) { +#pragma warning disable 0618 // SSL2/3 are deprecated + case SslProtocols.Ssl2: + curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_SSLv2; + break; + case SslProtocols.Ssl3: + curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_SSLv3; + break; +#pragma warning restore 0618 + case SslProtocols.Tls: curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_TLSv1_0; break; diff --git a/src/System.Net.Http/src/System/Net/Http/CurlHandler/CurlHandler.SslProvider.OSX.cs b/src/System.Net.Http/src/System/Net/Http/CurlHandler/CurlHandler.SslProvider.OSX.cs index 4b2c02904d..42b83fc6a3 100644 --- a/src/System.Net.Http/src/System/Net/Http/CurlHandler/CurlHandler.SslProvider.OSX.cs +++ b/src/System.Net.Http/src/System/Net/Http/CurlHandler/CurlHandler.SslProvider.OSX.cs @@ -101,16 +101,21 @@ namespace System.Net.Http return; } - // We explicitly disallow choosing SSL2/3. Make sure they were filtered out. - Debug.Assert((protocols & ~SecurityProtocol.AllowedSecurityProtocols) == 0, - "Disallowed protocols should have been filtered out."); - // libcurl supports options for either enabling all of the TLS1.* protocols or enabling - // just one of them; it doesn't currently support enabling two of the three, e.g. you can't + // just one protocol; it doesn't currently support enabling two of the three, e.g. you can't // pick TLS1.1 and TLS1.2 but not TLS1.0, but you can select just TLS1.2. Interop.Http.CurlSslVersion curlSslVersion; switch (protocols) { +#pragma warning disable 0618 // SSL2/3 are deprecated + case SslProtocols.Ssl2: + curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_SSLv2; + break; + case SslProtocols.Ssl3: + curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_SSLv3; + break; +#pragma warning restore 0618 + case SslProtocols.Tls: curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_TLSv1_0; break; diff --git a/src/System.Net.Http/src/System/Net/Http/CurlHandler/CurlHandler.cs b/src/System.Net.Http/src/System/Net/Http/CurlHandler/CurlHandler.cs index a63fe3991d..ba51560b68 100644 --- a/src/System.Net.Http/src/System/Net/Http/CurlHandler/CurlHandler.cs +++ b/src/System.Net.Http/src/System/Net/Http/CurlHandler/CurlHandler.cs @@ -308,7 +308,6 @@ namespace System.Net.Http get { return _sslProtocols; } set { - SecurityProtocol.ThrowOnNotAllowed(value, allowNone: true); CheckDisposedOrStarted(); _sslProtocols = value; } diff --git a/src/System.Net.Http/src/System/Net/Http/HttpClientHandler.Unix.cs b/src/System.Net.Http/src/System/Net/Http/HttpClientHandler.Unix.cs index de34df54c6..05477b2372 100644 --- a/src/System.Net.Http/src/System/Net/Http/HttpClientHandler.Unix.cs +++ b/src/System.Net.Http/src/System/Net/Http/HttpClientHandler.Unix.cs @@ -198,7 +198,6 @@ namespace System.Net.Http } else { - SecurityProtocol.ThrowOnNotAllowed(value, allowNone: true); ThrowForModifiedManagedSslOptionsIfStarted(); _socketsHttpHandler.SslOptions.EnabledSslProtocols = value; } diff --git a/src/System.Net.Http/src/System/Net/Http/HttpClientHandler.Windows.cs b/src/System.Net.Http/src/System/Net/Http/HttpClientHandler.Windows.cs index 1c0251c404..5ce147043f 100644 --- a/src/System.Net.Http/src/System/Net/Http/HttpClientHandler.Windows.cs +++ b/src/System.Net.Http/src/System/Net/Http/HttpClientHandler.Windows.cs @@ -427,7 +427,6 @@ namespace System.Net.Http } else { - SecurityProtocol.ThrowOnNotAllowed(value, allowNone: true); ThrowForModifiedManagedSslOptionsIfStarted(); _socketsHttpHandler.SslOptions.EnabledSslProtocols = value; } diff --git a/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs b/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs index 22e2610f45..1c0e5d734d 100644 --- a/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs +++ b/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs @@ -62,6 +62,11 @@ namespace System.Net.Http while (true) { string challengeResponse = authContext.GetOutgoingBlob(challengeData); + if (challengeResponse == null) + { + // Response indicated denial even after login, so stop processing and return current response. + break; + } await connection.DrainResponseAsync(response).ConfigureAwait(false); diff --git a/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs b/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs index c2df52fdb3..a64cc7c112 100644 --- a/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs +++ b/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Concurrent; using System.Diagnostics; using System.IO; using System.Net.Security; @@ -15,6 +16,11 @@ namespace System.Net.Http { internal static class ConnectHelper { + /// <summary>Pool of event args to use to establish connections.</summary> + private static readonly ConcurrentQueue<ConnectEventArgs>.Segment s_connectEventArgs = + new ConcurrentQueue<ConnectEventArgs>.Segment( + ConcurrentQueue<ConnectEventArgs>.Segment.RoundUpToPowerOf2(Environment.ProcessorCount)); + /// <summary> /// Helper type used by HttpClientHandler when wrapping SocketsHttpHandler to map its /// certificate validation callback to the one used by SslStream. @@ -34,64 +40,46 @@ namespace System.Net.Http public static async ValueTask<(Socket, Stream)> ConnectAsync(string host, int port, CancellationToken cancellationToken) { + // Rather than creating a new Socket and calling ConnectAsync on it, we use the static + // Socket.ConnectAsync with a SocketAsyncEventArgs, as we can then use Socket.CancelConnectAsync + // to cancel it if needed. Rent or allocate one. + ConnectEventArgs saea; + if (!s_connectEventArgs.TryDequeue(out saea)) + { + saea = new ConnectEventArgs(); + } + try { - // Rather than creating a new Socket and calling ConnectAsync on it, we use the static - // Socket.ConnectAsync with a SocketAsyncEventArgs, as we can then use Socket.CancelConnectAsync - // to cancel it if needed. - using (var saea = new BuilderAndCancellationTokenSocketAsyncEventArgs(cancellationToken)) - { - // Configure which server to which to connect. - saea.RemoteEndPoint = IPAddress.TryParse(host, out IPAddress address) ? - (EndPoint)new IPEndPoint(address, port) : - new DnsEndPoint(host, port); + saea.Initialize(cancellationToken); - // Hook up a callback that'll complete the Task when the operation completes. - saea.Completed += (s, e) => - { - var csaea = (BuilderAndCancellationTokenSocketAsyncEventArgs)e; - switch (e.SocketError) - { - case SocketError.Success: - csaea.Builder.SetResult(); - break; - case SocketError.OperationAborted: - case SocketError.ConnectionAborted: - if (csaea.CancellationToken.IsCancellationRequested) - { - csaea.Builder.SetException(CancellationHelper.CreateOperationCanceledException(null, csaea.CancellationToken)); - break; - } - goto default; - default: - csaea.Builder.SetException(new SocketException((int)e.SocketError)); - break; - } - }; + // Configure which server to which to connect. + saea.RemoteEndPoint = IPAddress.TryParse(host, out IPAddress address) ? + (EndPoint)new IPEndPoint(address, port) : + new DnsEndPoint(host, port); - // Initiate the connection. - if (Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, saea)) - { - // Connect completing asynchronously. Enable it to be canceled and wait for it. - using (cancellationToken.Register(s => Socket.CancelConnectAsync((SocketAsyncEventArgs)s), saea)) - { - await saea.Builder.Task.ConfigureAwait(false); - } - } - else if (saea.SocketError != SocketError.Success) + // Initiate the connection. + if (Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, saea)) + { + // Connect completing asynchronously. Enable it to be canceled and wait for it. + using (cancellationToken.Register(s => Socket.CancelConnectAsync((SocketAsyncEventArgs)s), saea)) { - // Connect completed synchronously but unsuccessfully. - throw new SocketException((int)saea.SocketError); + await saea.Builder.Task.ConfigureAwait(false); } - - Debug.Assert(saea.SocketError == SocketError.Success, $"Expected Success, got {saea.SocketError}."); - Debug.Assert(saea.ConnectSocket != null, "Expected non-null socket"); - - // Configure the socket and return a stream for it. - Socket socket = saea.ConnectSocket; - socket.NoDelay = true; - return (socket, new NetworkStream(socket, ownsSocket: true)); } + else if (saea.SocketError != SocketError.Success) + { + // Connect completed synchronously but unsuccessfully. + throw new SocketException((int)saea.SocketError); + } + + Debug.Assert(saea.SocketError == SocketError.Success, $"Expected Success, got {saea.SocketError}."); + Debug.Assert(saea.ConnectSocket != null, "Expected non-null socket"); + + // Configure the socket and return a stream for it. + Socket socket = saea.ConnectSocket; + socket.NoDelay = true; + return (socket, new NetworkStream(socket, ownsSocket: true)); } catch (Exception error) { @@ -99,21 +87,54 @@ namespace System.Net.Http CancellationHelper.CreateOperationCanceledException(error, cancellationToken) : new HttpRequestException(error.Message, error); } + finally + { + // Pool the event args, or if the pool is full, dispose of it. + saea.Clear(); + if (!s_connectEventArgs.TryEnqueue(saea)) + { + saea.Dispose(); + } + } } /// <summary>SocketAsyncEventArgs that carries with it additional state for a Task builder and a CancellationToken.</summary> - private sealed class BuilderAndCancellationTokenSocketAsyncEventArgs : SocketAsyncEventArgs + private sealed class ConnectEventArgs : SocketAsyncEventArgs { - public AsyncTaskMethodBuilder Builder { get; } - public CancellationToken CancellationToken { get; } + public AsyncTaskMethodBuilder Builder { get; private set; } + public CancellationToken CancellationToken { get; private set; } - public BuilderAndCancellationTokenSocketAsyncEventArgs(CancellationToken cancellationToken) + public void Initialize(CancellationToken cancellationToken) { + CancellationToken = cancellationToken; var b = new AsyncTaskMethodBuilder(); var ignored = b.Task; // force initialization Builder = b; + } - CancellationToken = cancellationToken; + public void Clear() => CancellationToken = default; + + protected override void OnCompleted(SocketAsyncEventArgs _) + { + switch (SocketError) + { + case SocketError.Success: + Builder.SetResult(); + break; + + case SocketError.OperationAborted: + case SocketError.ConnectionAborted: + if (CancellationToken.IsCancellationRequested) + { + Builder.SetException(CancellationHelper.CreateOperationCanceledException(null, CancellationToken)); + break; + } + goto default; + + default: + Builder.SetException(new SocketException((int)SocketError)); + break; + } } } diff --git a/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs b/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs index 93e2b2f4a5..8a590a4777 100644 --- a/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs +++ b/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs @@ -55,6 +55,7 @@ namespace System.Net.Http private int _allowedReadLineBytes; private ValueTask<int>? _readAheadTask; + private int _readAheadTaskLock = 0; // 0 == free, 1 == held private byte[] _readBuffer; private int _readOffset; private int _readLength; @@ -121,33 +122,42 @@ namespace System.Net.Http // Eat any exceptions from the read-ahead task. We don't need to log, as we expect // failures from this task due to closing the connection while a read is in progress. - if (_readAheadTask != null) + ValueTask<int>? readAheadTask = ConsumeReadAheadTask(); + if (readAheadTask != null) { - ValueTask<int> t = _readAheadTask.GetValueOrDefault(); - if (t.IsCompleted) - { - if (!t.IsCompletedSuccessfully) - { - Exception ignored = t.AsTask().Exception; // accessing Exception prop is sufficient to suppress unobserved exception events - } - } - else - { - t.AsTask().ContinueWith(p => - { - Exception ignored = p.Exception; - }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default); - } + IgnoreExceptionsAsync(readAheadTask.GetValueOrDefault()); } } } } + /// <summary>Awaits a task, ignoring any resulting exceptions.</summary> + private static async void IgnoreExceptionsAsync(ValueTask<int> task) + { + try { await task.ConfigureAwait(false); } catch { } + } + /// <summary>Do a non-blocking poll to see whether the connection has data available or has been closed.</summary> /// <remarks>If we don't have direct access to the underlying socket, we instead use a read-ahead task.</remarks> - public bool PollRead() => _socket != null ? - _socket.Poll(0, SelectMode.SelectRead) : - EnsureReadAheadAndPollRead(); + public bool PollRead() + { + if (_socket != null) // may be null if we don't have direct access to the socket + { + try + { + return _socket.Poll(0, SelectMode.SelectRead); + } + catch (Exception e) when (e is SocketException || e is ObjectDisposedException) + { + // Poll can throw when used on a closed socket. + return true; + } + } + else + { + return EnsureReadAheadAndPollRead(); + } + } /// <summary> /// Issues a read-ahead on the connection, which will serve both as the first read on the @@ -175,6 +185,21 @@ namespace System.Net.Http return _readAheadTask.Value.IsCompleted; // equivalent to polling } + private ValueTask<int>? ConsumeReadAheadTask() + { + if (Interlocked.CompareExchange(ref _readAheadTaskLock, 1, 0) == 0) + { + ValueTask<int>? t = _readAheadTask; + _readAheadTask = null; + Volatile.Write(ref _readAheadTaskLock, 0); + return t; + } + + // We couldn't get the lock, which means it must already be held + // by someone else who will consume the task. + return null; + } + public bool IsNewConnection { get @@ -438,16 +463,11 @@ namespace System.Net.Http // by the previous request handling. (Note we do not support HTTP pipelining.) Debug.Assert(_readOffset == _readLength); - // When the connection was put back into the pool, a pre-emptive read was performed - // into the read buffer. That read should not complete prior to us using the - // connection again, as that would mean the connection was either closed or had - // erroneous data sent on it by the server in response to no request from us. - // We need to consume that read prior to issuing another read request. - ValueTask<int>? t = _readAheadTask; + // When the connection was taken out of the pool, a pre-emptive read was performed + // into the read buffer. We need to consume that read prior to issuing another read. + ValueTask<int>? t = ConsumeReadAheadTask(); if (t != null) { - _readAheadTask = null; - int bytesRead = await t.GetValueOrDefault().ConfigureAwait(false); if (NetEventSource.IsEnabled) Trace($"Received {bytesRead} bytes."); diff --git a/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs b/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs index 1c624fcbc4..65e2bf405a 100644 --- a/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs +++ b/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs @@ -608,8 +608,9 @@ namespace System.Net.Http // If the pool has been disposed of, dispose the connection being returned, // as the pool is being deactivated. We do this after the above in order to // use pooled connections to satisfy any requests that pended before the - // the pool was disposed of. - if (_disposed) + // the pool was disposed of. We also dispose of connections if connection + // timeouts are such that the connection would immediately expire, anyway. + if (_disposed || _poolManager.AvoidStoringConnections) { if (NetEventSource.IsEnabled) connection.Trace("Disposing connection returned to disposed pool."); connection.Dispose(); diff --git a/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPoolManager.cs b/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPoolManager.cs index b4b80898f1..df1f4f50ad 100644 --- a/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPoolManager.cs +++ b/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPoolManager.cs @@ -5,7 +5,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; -using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; @@ -62,17 +61,41 @@ namespace System.Net.Http // Start out with the timer not running, since we have no pools. // When it does run, run it with a frequency based on the idle timeout. - if (settings._pooledConnectionIdleTimeout == Timeout.InfiniteTimeSpan) + if (!AvoidStoringConnections) { - const int DefaultScavengeSeconds = 30; - _cleanPoolTimeout = TimeSpan.FromSeconds(DefaultScavengeSeconds); - } - else - { - const int ScavengesPerIdle = 4; - const int MinScavengeSeconds = 1; - TimeSpan timerPeriod = settings._pooledConnectionIdleTimeout / ScavengesPerIdle; - _cleanPoolTimeout = timerPeriod.TotalSeconds >= MinScavengeSeconds ? timerPeriod : TimeSpan.FromSeconds(MinScavengeSeconds); + if (settings._pooledConnectionIdleTimeout == Timeout.InfiniteTimeSpan) + { + const int DefaultScavengeSeconds = 30; + _cleanPoolTimeout = TimeSpan.FromSeconds(DefaultScavengeSeconds); + } + else + { + const int ScavengesPerIdle = 4; + const int MinScavengeSeconds = 1; + TimeSpan timerPeriod = settings._pooledConnectionIdleTimeout / ScavengesPerIdle; + _cleanPoolTimeout = timerPeriod.TotalSeconds >= MinScavengeSeconds ? timerPeriod : TimeSpan.FromSeconds(MinScavengeSeconds); + } + + bool restoreFlow = false; + try + { + // Don't capture the current ExecutionContext and its AsyncLocals onto the timer causing them to live forever + if (!ExecutionContext.IsFlowSuppressed()) + { + ExecutionContext.SuppressFlow(); + restoreFlow = true; + } + + _cleaningTimer = new Timer(s => ((HttpConnectionPoolManager)s).RemoveStalePools(), this, Timeout.Infinite, Timeout.Infinite); + } + finally + { + // Restore the current ExecutionContext + if (restoreFlow) + { + ExecutionContext.RestoreFlow(); + } + } } // Figure out proxy stuff. @@ -84,29 +107,13 @@ namespace System.Net.Http _proxyCredentials = _proxy.Credentials ?? settings._defaultProxyCredentials; } } - - // Don't capture the current ExecutionContext and its AsyncLocals onto the timer causing them to live forever - bool restoreFlow = false; - try - { - if (!ExecutionContext.IsFlowSuppressed()) - { - ExecutionContext.SuppressFlow(); - restoreFlow = true; - } - - _cleaningTimer = new Timer(s => ((HttpConnectionPoolManager)s).RemoveStalePools(), this, Timeout.Infinite, Timeout.Infinite); - } - finally - { - // Restore the current ExecutionContext - if (restoreFlow) - ExecutionContext.RestoreFlow(); - } } public HttpConnectionSettings Settings => _settings; public ICredentials ProxyCredentials => _proxyCredentials; + public bool AvoidStoringConnections => + _settings._pooledConnectionIdleTimeout == TimeSpan.Zero || + _settings._pooledConnectionLifetime == TimeSpan.Zero; private static string ParseHostNameFromHeader(string hostHeader) { @@ -204,7 +211,7 @@ namespace System.Net.Http while (!_pools.TryGetValue(key, out pool)) { pool = new HttpConnectionPool(this, key.Kind, key.Host, key.Port, key.SslHostName, key.ProxyUri, _maxConnectionsPerServer); - if (_pools.TryAdd(key, pool)) + if (_pools.TryAdd(key, pool) && _cleaningTimer != null) { // We need to ensure the cleanup timer is running if it isn't // already now that we added a new connection pool. @@ -263,7 +270,8 @@ namespace System.Net.Http /// <summary>Disposes of the pools, disposing of each individual pool.</summary> public void Dispose() { - _cleaningTimer.Dispose(); + _cleaningTimer?.Dispose(); + foreach (KeyValuePair<HttpConnectionKey, HttpConnectionPool> pool in _pools) { pool.Value.Dispose(); @@ -278,6 +286,8 @@ namespace System.Net.Http /// <summary>Removes unusable connections from each pool, and removes stale pools entirely.</summary> private void RemoveStalePools() { + Debug.Assert(_cleaningTimer != null); + // Iterate through each pool in the set of pools. For each, ask it to clear out // any unusable connections (e.g. those which have expired, those which have been closed, etc.) // The pool may detect that it's empty and long unused, in which case it'll dispose of itself, diff --git a/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.SslProtocols.Unix.cs b/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.SslProtocols.Unix.cs index f10fdb75f2..615f2cb4fa 100644 --- a/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.SslProtocols.Unix.cs +++ b/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.SslProtocols.Unix.cs @@ -19,9 +19,5 @@ namespace System.Net.Http.Functional.Tests private bool BackendSupportsSslConfiguration => UseSocketsHttpHandler || (Interop.Http.GetSslVersionDescription()?.StartsWith(Interop.Http.OpenSsl10Description, StringComparison.OrdinalIgnoreCase) ?? false); - - private bool SSLv3DisabledByDefault => - BackendSupportsSslConfiguration || - Version.Parse(Interop.Http.GetVersionDescription()) >= new Version(7, 39); // libcurl disables SSLv3 by default starting in v7.39 } } diff --git a/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.SslProtocols.Windows.cs b/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.SslProtocols.Windows.cs index 020d9c5afa..961275bfc0 100644 --- a/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.SslProtocols.Windows.cs +++ b/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.SslProtocols.Windows.cs @@ -8,7 +8,5 @@ namespace System.Net.Http.Functional.Tests public abstract partial class HttpClientHandler_SslProtocols_Test { private static bool BackendSupportsSslConfiguration => true; - - private static bool SSLv3DisabledByDefault => true; } } diff --git a/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.SslProtocols.cs b/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.SslProtocols.cs index 25fd78fcb1..e578aa7597 100644 --- a/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.SslProtocols.cs +++ b/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.SslProtocols.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; using System.IO; using System.Net.Security; using System.Net.Test.Common; @@ -68,31 +69,35 @@ namespace System.Net.Http.Functional.Tests } } - [OuterLoop] // TODO: Issue #11345 - [Theory] - [InlineData(~SslProtocols.None)] -#pragma warning disable 0618 // obsolete warning - [InlineData(SslProtocols.Ssl2)] - [InlineData(SslProtocols.Ssl3)] - [InlineData(SslProtocols.Ssl2 | SslProtocols.Ssl3)] - [InlineData(SslProtocols.Ssl2 | SslProtocols.Ssl3 | SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12)] -#pragma warning restore 0618 - public void DisabledProtocols_SetSslProtocols_ThrowsException(SslProtocols disabledProtocols) + + public static IEnumerable<object[]> GetAsync_AllowedSSLVersion_Succeeds_MemberData() { - using (HttpClientHandler handler = CreateHttpClientHandler()) + // These protocols are all enabled by default, so we can connect with them both when + // explicitly specifying it in the client and when not. + foreach (SslProtocols protocol in new[] { SslProtocols.Tls, SslProtocols.Tls11, SslProtocols.Tls12 }) { - Assert.Throws<NotSupportedException>(() => handler.SslProtocols = disabledProtocols); + yield return new object[] { protocol, false }; + yield return new object[] { protocol, true }; } + + // These protocols are disabled by default, so we can only connect with them explicitly +#pragma warning disable 0618 + if (PlatformDetection.IsWindows || + PlatformDetection.IsOSX || + (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && PlatformDetection.OpenSslVersion < new Version(1, 0, 2) && !PlatformDetection.IsDebian)) + { + yield return new object[] { SslProtocols.Ssl3, true }; + } + if (PlatformDetection.IsWindows && !PlatformDetection.IsWindows10Version1607OrGreater) + { + yield return new object[] { SslProtocols.Ssl2, true }; + } +#pragma warning restore 0618 } [OuterLoop] // TODO: Issue #11345 [Theory] - [InlineData(SslProtocols.Tls, false)] - [InlineData(SslProtocols.Tls, true)] - [InlineData(SslProtocols.Tls11, false)] - [InlineData(SslProtocols.Tls11, true)] - [InlineData(SslProtocols.Tls12, false)] - [InlineData(SslProtocols.Tls12, true)] + [MemberData(nameof(GetAsync_AllowedSSLVersion_Succeeds_MemberData))] public async Task GetAsync_AllowedSSLVersion_Succeeds(SslProtocols acceptedProtocol, bool requestOnlyThisProtocol) { if (!BackendSupportsSslConfiguration) @@ -125,17 +130,24 @@ namespace System.Net.Http.Functional.Tests } } - public static readonly object[][] SupportedSSLVersionServers = + public static IEnumerable<object[]> SupportedSSLVersionServers() { - new object[] {SslProtocols.Tls, Configuration.Http.TLSv10RemoteServer}, - new object[] {SslProtocols.Tls11, Configuration.Http.TLSv11RemoteServer}, - new object[] {SslProtocols.Tls12, Configuration.Http.TLSv12RemoteServer}, - }; +#pragma warning disable 0618 // SSL2/3 are deprecated + if (PlatformDetection.IsWindows || + PlatformDetection.IsOSX || + (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && PlatformDetection.OpenSslVersion < new Version(1, 0, 2) && !PlatformDetection.IsDebian)) + { + yield return new object[] { SslProtocols.Ssl3, Configuration.Http.SSLv3RemoteServer }; + } +#pragma warning restore 0618 + yield return new object[] { SslProtocols.Tls, Configuration.Http.TLSv10RemoteServer }; + yield return new object[] { SslProtocols.Tls11, Configuration.Http.TLSv11RemoteServer }; + yield return new object[] { SslProtocols.Tls12, Configuration.Http.TLSv12RemoteServer }; + } - // This test is logically the same as the above test, albeit using remote servers - // instead of local ones. We're keeping it for now (as outerloop) because it helps - // to validate against another SSL implementation that what we mean by a particular - // TLS version matches that other implementation. + // We have tests that validate with SslStream, but that's limited by what the current OS supports. + // This tests provides additional validation against an external server. + [ActiveIssue(26186)] [OuterLoop("Avoid www.ssllabs.com dependency in innerloop.")] [Theory] [MemberData(nameof(SupportedSSLVersionServers))] @@ -149,12 +161,7 @@ namespace System.Net.Http.Functional.Tests using (HttpClientHandler handler = CreateHttpClientHandler()) { - if (PlatformDetection.IsRedHatFamily7) - { - // Default protocol selection is always TLSv1 on Centos7 libcurl 7.29.0 - // Hence, set the specific protocol on HttpClient that is required by test - handler.SslProtocols = sslProtocols; - } + handler.SslProtocols = sslProtocols; using (var client = new HttpClient(handler)) { (await RemoteServerQuery.Run(() => client.GetAsync(url), remoteServerExceptionWrapper, url)).Dispose(); @@ -177,35 +184,27 @@ namespace System.Net.Http.Functional.Tests } }; - public static readonly object[][] NotSupportedSSLVersionServers = + public static IEnumerable<object[]> NotSupportedSSLVersionServers() { - new object[] {"SSLv2", Configuration.Http.SSLv2RemoteServer}, - new object[] {"SSLv3", Configuration.Http.SSLv3RemoteServer}, - }; +#pragma warning disable 0618 + if (PlatformDetection.IsWindows10Version1607OrGreater) + { + yield return new object[] { SslProtocols.Ssl2, Configuration.Http.SSLv2RemoteServer }; + } +#pragma warning restore 0618 + } - // It would be easy to remove the dependency on these remote servers if we didn't - // explicitly disallow creating SslStream with SSLv2/3. Since we explicitly throw - // when trying to use such an SslStream, we can't stand up a localhost server that - // only speaks those protocols. + // We have tests that validate with SslStream, but that's limited by what the current OS supports. + // This tests provides additional validation against an external server. [OuterLoop("Avoid www.ssllabs.com dependency in innerloop.")] [Theory] [MemberData(nameof(NotSupportedSSLVersionServers))] - public async Task GetAsync_UnsupportedSSLVersion_Throws(string name, string url) + public async Task GetAsync_UnsupportedSSLVersion_Throws(SslProtocols sslProtocols, string url) { - if (!SSLv3DisabledByDefault) - { - return; - } - - if (UseSocketsHttpHandler && !PlatformDetection.IsWindows10Version1607OrGreater) - { - // On Windows, https://github.com/dotnet/corefx/issues/21925#issuecomment-313408314 - // On Linux, an older version of OpenSSL may permit negotiating SSLv3. - return; - } - - using (HttpClient client = CreateHttpClient()) + using (HttpClientHandler handler = CreateHttpClientHandler()) + using (HttpClient client = new HttpClient(handler)) { + handler.SslProtocols = sslProtocols; await Assert.ThrowsAsync<HttpRequestException>(() => RemoteServerQuery.Run(() => client.GetAsync(url), remoteServerExceptionWrapper, url)); } } @@ -240,6 +239,10 @@ namespace System.Net.Http.Functional.Tests [OuterLoop] // TODO: Issue #11345 [Theory] +#pragma warning disable 0618 // SSL2/3 are deprecated + [InlineData(SslProtocols.Ssl2, SslProtocols.Tls12)] + [InlineData(SslProtocols.Ssl3, SslProtocols.Tls12)] +#pragma warning restore 0618 [InlineData(SslProtocols.Tls11, SslProtocols.Tls)] [InlineData(SslProtocols.Tls12, SslProtocols.Tls11)] [InlineData(SslProtocols.Tls, SslProtocols.Tls12)] diff --git a/src/System.Net.Http/tests/FunctionalTests/ReadOnlyMemoryContentTest.cs b/src/System.Net.Http/tests/FunctionalTests/ReadOnlyMemoryContentTest.cs index f33fc4bb74..cc2e7e9d9e 100644 --- a/src/System.Net.Http/tests/FunctionalTests/ReadOnlyMemoryContentTest.cs +++ b/src/System.Net.Http/tests/FunctionalTests/ReadOnlyMemoryContentTest.cs @@ -35,12 +35,10 @@ namespace System.Net.Http.Functional.Tests public void ContentLength_LengthMatchesArrayLength(int contentLength, bool useArray) { Memory<byte> memory; - OwnedMemory<byte> ownedMemory; - ReadOnlyMemoryContent content = CreateContent(contentLength, useArray, out memory, out ownedMemory); - - Assert.Equal(contentLength, content.Headers.ContentLength); - - ownedMemory?.Dispose(); + using (ReadOnlyMemoryContent content = CreateContent(contentLength, useArray, out memory, out MemoryManager<byte> ownedMemory)) + { + Assert.Equal(contentLength, content.Headers.ContentLength); + } } [Theory] @@ -49,32 +47,32 @@ namespace System.Net.Http.Functional.Tests { const int ContentLength = 42; Memory<byte> memory; - OwnedMemory<byte> ownedMemory; - ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out memory, out ownedMemory); - using (Stream stream = await content.ReadAsStreamAsync()) + using (ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out memory, out MemoryManager<byte> ownedMemory)) { - // property values - Assert.Equal(ContentLength, stream.Length); - Assert.Equal(0, stream.Position); - Assert.True(stream.CanRead); - Assert.True(stream.CanSeek); - Assert.False(stream.CanWrite); - - // not supported - Assert.Throws<NotSupportedException>(() => stream.SetLength(12345)); - Assert.Throws<NotSupportedException>(() => stream.WriteByte(0)); - Assert.Throws<NotSupportedException>(() => stream.Write(new byte[1], 0, 1)); - Assert.Throws<NotSupportedException>(() => stream.Write(new ReadOnlySpan<byte>(new byte[1]))); - await Assert.ThrowsAsync<NotSupportedException>(async () => await stream.WriteAsync(new byte[1], 0, 1)); - await Assert.ThrowsAsync<NotSupportedException>(async () => await stream.WriteAsync(new ReadOnlyMemory<byte>(new byte[1]))); - - // nops - stream.Flush(); - await stream.FlushAsync(); - } + using (Stream stream = await content.ReadAsStreamAsync()) + { - ownedMemory?.Dispose(); + // property values + Assert.Equal(ContentLength, stream.Length); + Assert.Equal(0, stream.Position); + Assert.True(stream.CanRead); + Assert.True(stream.CanSeek); + Assert.False(stream.CanWrite); + + // not supported + Assert.Throws<NotSupportedException>(() => stream.SetLength(12345)); + Assert.Throws<NotSupportedException>(() => stream.WriteByte(0)); + Assert.Throws<NotSupportedException>(() => stream.Write(new byte[1], 0, 1)); + Assert.Throws<NotSupportedException>(() => stream.Write(new ReadOnlySpan<byte>(new byte[1]))); + await Assert.ThrowsAsync<NotSupportedException>(async () => await stream.WriteAsync(new byte[1], 0, 1)); + await Assert.ThrowsAsync<NotSupportedException>(async () => await stream.WriteAsync(new ReadOnlyMemory<byte>(new byte[1]))); + + // nops + stream.Flush(); + await stream.FlushAsync(); + } + } } [Theory] @@ -83,62 +81,61 @@ namespace System.Net.Http.Functional.Tests { const int ContentLength = 42; Memory<byte> memory; - OwnedMemory<byte> ownedMemory; - ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out memory, out ownedMemory); - using (Stream s = await content.ReadAsStreamAsync()) + using (ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out memory, out MemoryManager<byte> ownedMemory)) { - foreach (int pos in new[] { 0, ContentLength / 2, ContentLength - 1 }) + using (Stream s = await content.ReadAsStreamAsync()) { - s.Position = pos; - Assert.Equal(pos, s.Position); - Assert.Equal(memory.Span[pos], s.ReadByte()); - } + foreach (int pos in new[] { 0, ContentLength / 2, ContentLength - 1 }) + { + s.Position = pos; + Assert.Equal(pos, s.Position); + Assert.Equal(memory.Span[pos], s.ReadByte()); + } - foreach (int pos in new[] { 0, ContentLength / 2, ContentLength - 1 }) - { - Assert.Equal(0, s.Seek(0, SeekOrigin.Begin)); + foreach (int pos in new[] { 0, ContentLength / 2, ContentLength - 1 }) + { + Assert.Equal(0, s.Seek(0, SeekOrigin.Begin)); + Assert.Equal(memory.Span[0], s.ReadByte()); + } + + Assert.Equal(ContentLength, s.Seek(0, SeekOrigin.End)); + Assert.Equal(s.Position, s.Length); + Assert.Equal(-1, s.ReadByte()); + + Assert.Equal(0, s.Seek(-ContentLength, SeekOrigin.End)); + Assert.Equal(0, s.Position); Assert.Equal(memory.Span[0], s.ReadByte()); - } - Assert.Equal(ContentLength, s.Seek(0, SeekOrigin.End)); - Assert.Equal(s.Position, s.Length); - Assert.Equal(-1, s.ReadByte()); - - Assert.Equal(0, s.Seek(-ContentLength, SeekOrigin.End)); - Assert.Equal(0, s.Position); - Assert.Equal(memory.Span[0], s.ReadByte()); - - s.Position = 0; - Assert.Equal(0, s.Seek(0, SeekOrigin.Current)); - Assert.Equal(0, s.Position); - - Assert.Equal(1, s.Seek(1, SeekOrigin.Current)); - Assert.Equal(1, s.Position); - Assert.Equal(memory.Span[1], s.ReadByte()); - Assert.Equal(2, s.Position); - Assert.Equal(3, s.Seek(1, SeekOrigin.Current)); - Assert.Equal(1, s.Seek(-2, SeekOrigin.Current)); - - Assert.Equal(int.MaxValue, s.Seek(int.MaxValue, SeekOrigin.Begin)); - Assert.Equal(int.MaxValue, s.Position); - Assert.Equal(int.MaxValue, s.Seek(0, SeekOrigin.Current)); - Assert.Equal(int.MaxValue, s.Position); - Assert.Equal(int.MaxValue, s.Seek(int.MaxValue - ContentLength, SeekOrigin.End)); - Assert.Equal(int.MaxValue, s.Position); - Assert.Equal(-1, s.ReadByte()); - Assert.Equal(int.MaxValue, s.Position); - - Assert.Throws<ArgumentOutOfRangeException>("value", () => s.Position = -1); - Assert.Throws<IOException>(() => s.Seek(-1, SeekOrigin.Begin)); - - AssertExtensions.Throws<ArgumentOutOfRangeException>("value", () => s.Position = (long)int.MaxValue + 1); - AssertExtensions.Throws<ArgumentOutOfRangeException>("offset", () => s.Seek((long)int.MaxValue + 1, SeekOrigin.Begin)); - - Assert.ThrowsAny<ArgumentException>(() => s.Seek(0, (SeekOrigin)42)); + s.Position = 0; + Assert.Equal(0, s.Seek(0, SeekOrigin.Current)); + Assert.Equal(0, s.Position); + + Assert.Equal(1, s.Seek(1, SeekOrigin.Current)); + Assert.Equal(1, s.Position); + Assert.Equal(memory.Span[1], s.ReadByte()); + Assert.Equal(2, s.Position); + Assert.Equal(3, s.Seek(1, SeekOrigin.Current)); + Assert.Equal(1, s.Seek(-2, SeekOrigin.Current)); + + Assert.Equal(int.MaxValue, s.Seek(int.MaxValue, SeekOrigin.Begin)); + Assert.Equal(int.MaxValue, s.Position); + Assert.Equal(int.MaxValue, s.Seek(0, SeekOrigin.Current)); + Assert.Equal(int.MaxValue, s.Position); + Assert.Equal(int.MaxValue, s.Seek(int.MaxValue - ContentLength, SeekOrigin.End)); + Assert.Equal(int.MaxValue, s.Position); + Assert.Equal(-1, s.ReadByte()); + Assert.Equal(int.MaxValue, s.Position); + + Assert.Throws<ArgumentOutOfRangeException>("value", () => s.Position = -1); + Assert.Throws<IOException>(() => s.Seek(-1, SeekOrigin.Begin)); + + AssertExtensions.Throws<ArgumentOutOfRangeException>("value", () => s.Position = (long)int.MaxValue + 1); + AssertExtensions.Throws<ArgumentOutOfRangeException>("offset", () => s.Seek((long)int.MaxValue + 1, SeekOrigin.Begin)); + + Assert.ThrowsAny<ArgumentException>(() => s.Seek(0, (SeekOrigin)42)); + } } - - ownedMemory?.Dispose(); } [Theory] @@ -146,21 +143,20 @@ namespace System.Net.Http.Functional.Tests public async Task ReadAsStreamAsync_ReadByte_MatchesInput(int contentLength, bool useArray) { Memory<byte> memory; - OwnedMemory<byte> ownedMemory; - ReadOnlyMemoryContent content = CreateContent(contentLength, useArray, out memory, out ownedMemory); - using (Stream stream = await content.ReadAsStreamAsync()) + using (ReadOnlyMemoryContent content = CreateContent(contentLength, useArray, out memory, out MemoryManager<byte> ownedMemory)) { - for (int i = 0; i < contentLength; i++) + using (Stream stream = await content.ReadAsStreamAsync()) { - Assert.Equal(memory.Span[i], stream.ReadByte()); - Assert.Equal(i + 1, stream.Position); + for (int i = 0; i < contentLength; i++) + { + Assert.Equal(memory.Span[i], stream.ReadByte()); + Assert.Equal(i + 1, stream.Position); + } + Assert.Equal(-1, stream.ReadByte()); + Assert.Equal(stream.Length, stream.Position); } - Assert.Equal(-1, stream.ReadByte()); - Assert.Equal(stream.Length, stream.Position); } - - ownedMemory?.Dispose(); } [Theory] @@ -169,27 +165,26 @@ namespace System.Net.Http.Functional.Tests { const int ContentLength = 42; Memory<byte> memory; - OwnedMemory<byte> ownedMemory; - ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out memory, out ownedMemory); - using (Stream stream = await content.ReadAsStreamAsync()) + using (ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out memory, out MemoryManager<byte> ownedMemory)) { - AssertExtensions.Throws<ArgumentNullException>("buffer", () => stream.Read(null, 0, 0)); - AssertExtensions.Throws<ArgumentNullException>("buffer", () => { stream.ReadAsync(null, 0, 0); }); + using (Stream stream = await content.ReadAsStreamAsync()) + { + AssertExtensions.Throws<ArgumentNullException>("buffer", () => stream.Read(null, 0, 0)); + AssertExtensions.Throws<ArgumentNullException>("buffer", () => { stream.ReadAsync(null, 0, 0); }); - AssertExtensions.Throws<ArgumentOutOfRangeException>("offset", () => stream.Read(new byte[1], -1, 1)); - AssertExtensions.Throws<ArgumentOutOfRangeException>("offset", () => stream.Read(new byte[1], -1, 1)); + AssertExtensions.Throws<ArgumentOutOfRangeException>("offset", () => stream.Read(new byte[1], -1, 1)); + AssertExtensions.Throws<ArgumentOutOfRangeException>("offset", () => stream.Read(new byte[1], -1, 1)); - AssertExtensions.Throws<ArgumentOutOfRangeException>("count", () => stream.Read(new byte[1], 0, -1)); - AssertExtensions.Throws<ArgumentOutOfRangeException>("count", () => stream.Read(new byte[1], 0, -1)); + AssertExtensions.Throws<ArgumentOutOfRangeException>("count", () => stream.Read(new byte[1], 0, -1)); + AssertExtensions.Throws<ArgumentOutOfRangeException>("count", () => stream.Read(new byte[1], 0, -1)); - Assert.ThrowsAny<ArgumentException>(() => { stream.ReadAsync(new byte[1], 2, 0); }); - Assert.ThrowsAny<ArgumentException>(() => { stream.ReadAsync(new byte[1], 2, 0); }); - Assert.ThrowsAny<ArgumentException>(() => { stream.ReadAsync(new byte[1], 0, 2); }); - Assert.ThrowsAny<ArgumentException>(() => { stream.ReadAsync(new byte[1], 0, 2); }); + Assert.ThrowsAny<ArgumentException>(() => { stream.ReadAsync(new byte[1], 2, 0); }); + Assert.ThrowsAny<ArgumentException>(() => { stream.ReadAsync(new byte[1], 2, 0); }); + Assert.ThrowsAny<ArgumentException>(() => { stream.ReadAsync(new byte[1], 0, 2); }); + Assert.ThrowsAny<ArgumentException>(() => { stream.ReadAsync(new byte[1], 0, 2); }); + } } - - ownedMemory?.Dispose(); } [Theory] @@ -208,40 +203,38 @@ namespace System.Net.Http.Functional.Tests const int ContentLength = 1024; Memory<byte> memory; - OwnedMemory<byte> ownedMemory; - ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out memory, out ownedMemory); - - var buffer = new byte[3]; - - using (Stream stream = await content.ReadAsStreamAsync()) + using (ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out memory, out MemoryManager<byte> ownedMemory)) { - for (int i = 0; i < ContentLength; i += buffer.Length) + var buffer = new byte[3]; + + using (Stream stream = await content.ReadAsStreamAsync()) { - int bytesRead = + for (int i = 0; i < ContentLength; i += buffer.Length) + { + int bytesRead = + mode == 0 ? stream.Read(buffer, 0, buffer.Length) : + mode == 1 ? stream.Read(new Span<byte>(buffer)) : + mode == 2 ? await stream.ReadAsync(buffer, 0, buffer.Length) : + mode == 3 ? await stream.ReadAsync(new Memory<byte>(buffer)) : + await Task.Factory.FromAsync(stream.BeginRead, stream.EndRead, buffer, 0, buffer.Length, null); + + Assert.Equal(Math.Min(buffer.Length, ContentLength - i), bytesRead); + for (int j = 0; j < bytesRead; j++) + { + Assert.Equal(memory.Span[i + j], buffer[j]); + } + + Assert.Equal(i + bytesRead, stream.Position); + } + + Assert.Equal(0, mode == 0 ? stream.Read(buffer, 0, buffer.Length) : mode == 1 ? stream.Read(new Span<byte>(buffer)) : mode == 2 ? await stream.ReadAsync(buffer, 0, buffer.Length) : mode == 3 ? await stream.ReadAsync(new Memory<byte>(buffer)) : - await Task.Factory.FromAsync(stream.BeginRead, stream.EndRead, buffer, 0, buffer.Length, null); - - Assert.Equal(Math.Min(buffer.Length, ContentLength - i), bytesRead); - for (int j = 0; j < bytesRead; j++) - { - Assert.Equal(memory.Span[i + j], buffer[j]); - } - - Assert.Equal(i + bytesRead, stream.Position); + await Task.Factory.FromAsync(stream.BeginRead, stream.EndRead, buffer, 0, buffer.Length, null)); } - - Assert.Equal(0, - mode == 0 ? stream.Read(buffer, 0, buffer.Length) : - mode == 1 ? stream.Read(new Span<byte>(buffer)) : - mode == 2 ? await stream.ReadAsync(buffer, 0, buffer.Length) : - mode == 3 ? await stream.ReadAsync(new Memory<byte>(buffer)) : - await Task.Factory.FromAsync(stream.BeginRead, stream.EndRead, buffer, 0, buffer.Length, null)); } - - ownedMemory?.Dispose(); } [Theory] @@ -251,32 +244,30 @@ namespace System.Net.Http.Functional.Tests const int ContentLength = 100; Memory<byte> memory; - OwnedMemory<byte> ownedMemory; - ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out memory, out ownedMemory); - - var buffer = new byte[1]; - var cts = new CancellationTokenSource(); - int bytesRead; - - using (Stream stream = await content.ReadAsStreamAsync()) + using (ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out memory, out MemoryManager<byte> ownedMemory)) { - for (int i = 0; i < ContentLength; i++) + var buffer = new byte[1]; + var cts = new CancellationTokenSource(); + int bytesRead; + + using (Stream stream = await content.ReadAsStreamAsync()) { - switch (i % 2) + for (int i = 0; i < ContentLength; i++) { - case 0: - bytesRead = await stream.ReadAsync(buffer, 0, 1, cts.Token); - break; - default: - bytesRead = await stream.ReadAsync(new Memory<byte>(buffer), cts.Token); - break; + switch (i % 2) + { + case 0: + bytesRead = await stream.ReadAsync(buffer, 0, 1, cts.Token); + break; + default: + bytesRead = await stream.ReadAsync(new Memory<byte>(buffer), cts.Token); + break; + } + Assert.Equal(1, bytesRead); + Assert.Equal(memory.Span[i], buffer[0]); } - Assert.Equal(1, bytesRead); - Assert.Equal(memory.Span[i], buffer[0]); } } - - ownedMemory?.Dispose(); } [Theory] @@ -286,17 +277,16 @@ namespace System.Net.Http.Functional.Tests const int ContentLength = 2; Memory<byte> memory; - OwnedMemory<byte> ownedMemory; - ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out memory, out ownedMemory); - using (Stream stream = await content.ReadAsStreamAsync()) + using (ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out memory, out MemoryManager<byte> ownedMemory)) { - await Assert.ThrowsAnyAsync<OperationCanceledException>(() => stream.ReadAsync(new byte[1], 0, 1, new CancellationToken(true))); - await Assert.ThrowsAnyAsync<OperationCanceledException>(async () => await stream.ReadAsync(new Memory<byte>(new byte[1]), new CancellationToken(true))); - await Assert.ThrowsAnyAsync<OperationCanceledException>(async () => await stream.CopyToAsync(new MemoryStream(), 1, new CancellationToken(true))); + using (Stream stream = await content.ReadAsStreamAsync()) + { + await Assert.ThrowsAnyAsync<OperationCanceledException>(() => stream.ReadAsync(new byte[1], 0, 1, new CancellationToken(true))); + await Assert.ThrowsAnyAsync<OperationCanceledException>(async () => await stream.ReadAsync(new Memory<byte>(new byte[1]), new CancellationToken(true))); + await Assert.ThrowsAnyAsync<OperationCanceledException>(async () => await stream.CopyToAsync(new MemoryStream(), 1, new CancellationToken(true))); + } } - - ownedMemory?.Dispose(); } [Theory] @@ -304,15 +294,14 @@ namespace System.Net.Http.Functional.Tests public async Task CopyToAsync_AllContentCopied(int contentLength, bool useArray) { Memory<byte> memory; - OwnedMemory<byte> ownedMemory; - ReadOnlyMemoryContent content = CreateContent(contentLength, useArray, out memory, out ownedMemory); - - var destination = new MemoryStream(); - await content.CopyToAsync(destination); + using (ReadOnlyMemoryContent content = CreateContent(contentLength, useArray, out memory, out MemoryManager<byte> ownedMemory)) + { - Assert.Equal<byte>(memory.ToArray(), destination.ToArray()); + var destination = new MemoryStream(); + await content.CopyToAsync(destination); - ownedMemory?.Dispose(); + Assert.Equal<byte>(memory.ToArray(), destination.ToArray()); + } } [Theory] @@ -320,18 +309,17 @@ namespace System.Net.Http.Functional.Tests public async Task ReadAsStreamAsync_CopyTo_AllContentCopied(int contentLength, bool useArray) { Memory<byte> memory; - OwnedMemory<byte> ownedMemory; - ReadOnlyMemoryContent content = CreateContent(contentLength, useArray, out memory, out ownedMemory); - - var destination = new MemoryStream(); - using (Stream s = await content.ReadAsStreamAsync()) + using (ReadOnlyMemoryContent content = CreateContent(contentLength, useArray, out memory, out MemoryManager<byte> ownedMemory)) { - s.CopyTo(destination); - } - Assert.Equal<byte>(memory.ToArray(), destination.ToArray()); + var destination = new MemoryStream(); + using (Stream s = await content.ReadAsStreamAsync()) + { + s.CopyTo(destination); + } - ownedMemory?.Dispose(); + Assert.Equal<byte>(memory.ToArray(), destination.ToArray()); + } } [Theory] @@ -340,27 +328,25 @@ namespace System.Net.Http.Functional.Tests { const int ContentLength = 42; Memory<byte> memory; - OwnedMemory<byte> ownedMemory; - ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out memory, out ownedMemory); - - using (Stream s = await content.ReadAsStreamAsync()) + using (ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out memory, out MemoryManager<byte> ownedMemory)) { - AssertExtensions.Throws<ArgumentNullException>("destination", () => s.CopyTo(null)); - AssertExtensions.Throws<ArgumentNullException>("destination", () => { s.CopyToAsync(null); }); + using (Stream s = await content.ReadAsStreamAsync()) + { + AssertExtensions.Throws<ArgumentNullException>("destination", () => s.CopyTo(null)); + AssertExtensions.Throws<ArgumentNullException>("destination", () => { s.CopyToAsync(null); }); - AssertExtensions.Throws<ArgumentOutOfRangeException>("bufferSize", () => s.CopyTo(new MemoryStream(), 0)); - AssertExtensions.Throws<ArgumentOutOfRangeException>("bufferSize", () => { s.CopyToAsync(new MemoryStream(), 0); }); + AssertExtensions.Throws<ArgumentOutOfRangeException>("bufferSize", () => s.CopyTo(new MemoryStream(), 0)); + AssertExtensions.Throws<ArgumentOutOfRangeException>("bufferSize", () => { s.CopyToAsync(new MemoryStream(), 0); }); - Assert.Throws<NotSupportedException>(() => s.CopyTo(new MemoryStream(new byte[1], writable:false))); - Assert.Throws<NotSupportedException>(() => { s.CopyToAsync(new MemoryStream(new byte[1], writable: false)); }); + Assert.Throws<NotSupportedException>(() => s.CopyTo(new MemoryStream(new byte[1], writable: false))); + Assert.Throws<NotSupportedException>(() => { s.CopyToAsync(new MemoryStream(new byte[1], writable: false)); }); - var disposedDestination = new MemoryStream(); - disposedDestination.Dispose(); - Assert.Throws<ObjectDisposedException>(() => s.CopyTo(disposedDestination)); - Assert.Throws<ObjectDisposedException>(() => { s.CopyToAsync(disposedDestination); }); + var disposedDestination = new MemoryStream(); + disposedDestination.Dispose(); + Assert.Throws<ObjectDisposedException>(() => s.CopyTo(disposedDestination)); + Assert.Throws<ObjectDisposedException>(() => { s.CopyToAsync(disposedDestination); }); + } } - - ownedMemory?.Dispose(); } [Theory] @@ -368,21 +354,20 @@ namespace System.Net.Http.Functional.Tests public async Task ReadAsStreamAsync_CopyToAsync_AllContentCopied(int contentLength, bool useArray) { Memory<byte> memory; - OwnedMemory<byte> ownedMemory; - ReadOnlyMemoryContent content = CreateContent(contentLength, useArray, out memory, out ownedMemory); - - var destination = new MemoryStream(); - using (Stream s = await content.ReadAsStreamAsync()) + using (ReadOnlyMemoryContent content = CreateContent(contentLength, useArray, out memory, out MemoryManager<byte> ownedMemory)) { - await s.CopyToAsync(destination); - } - Assert.Equal<byte>(memory.ToArray(), destination.ToArray()); + var destination = new MemoryStream(); + using (Stream s = await content.ReadAsStreamAsync()) + { + await s.CopyToAsync(destination); + } - ownedMemory?.Dispose(); + Assert.Equal<byte>(memory.ToArray(), destination.ToArray()); + } } - private static ReadOnlyMemoryContent CreateContent(int contentLength, bool useArray, out Memory<byte> memory, out OwnedMemory<byte> ownedMemory) + private static ReadOnlyMemoryContent CreateContent(int contentLength, bool useArray, out Memory<byte> memory, out MemoryManager<byte> ownedMemory) { if (useArray) { @@ -391,7 +376,7 @@ namespace System.Net.Http.Functional.Tests } else { - ownedMemory = new NativeOwnedMemory(contentLength); + ownedMemory = new NativeMemoryManager(contentLength); memory = ownedMemory.Memory; } diff --git a/src/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj b/src/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj index 6e9a8807ef..b81d55947a 100644 --- a/src/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj +++ b/src/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj @@ -24,8 +24,8 @@ <Compile Include="$(CommonPath)\Interop\Unix\System.Net.Http.Native\Interop.VersionInfo.cs" Condition="'$(TargetsUnix)' == 'true'"> <Link>Common\Interop\Unix\System.Net.Http.Native\Interop.VersionInfo.cs</Link> </Compile> - <Compile Include="$(CommonTestPath)\System\Buffers\NativeOwnedMemory.cs"> - <Link>Common\System\Buffers\NativeOwnedMemory.cs</Link> + <Compile Include="$(CommonTestPath)\System\Buffers\NativeMemoryManager.cs"> + <Link>Common\System\Buffers\NativeMemoryManager.cs</Link> </Compile> <Compile Include="$(CommonTestPath)\System\Diagnostics\Tracing\TestEventListener.cs"> <Link>Common\System\Diagnostics\Tracing\TestEventListener.cs</Link> diff --git a/src/System.Net.HttpListener/src/System/Net/Managed/WebSockets/HttpWebSocket.Managed.cs b/src/System.Net.HttpListener/src/System/Net/Managed/WebSockets/HttpWebSocket.Managed.cs index d7c9ea6e58..f169e85717 100644 --- a/src/System.Net.HttpListener/src/System/Net/Managed/WebSockets/HttpWebSocket.Managed.cs +++ b/src/System.Net.HttpListener/src/System/Net/Managed/WebSockets/HttpWebSocket.Managed.cs @@ -58,12 +58,7 @@ namespace System.Net.WebSockets // Send websocket handshake headers await responseStream.WriteWebSocketHandshakeHeadersAsync().ConfigureAwait(false); - const int MinBufferSize = 14; // from ManagedWebSocket.MaxMessageHeaderLength - Memory<byte> buffer = - internalBuffer.GetValueOrDefault().Count >= MinBufferSize ? internalBuffer.GetValueOrDefault() : // use provided buffer if it's big enough - receiveBufferSize >= MinBufferSize ? new byte[receiveBufferSize] : // or use provided size if it's big enough - Memory<byte>.Empty; // or use the default - WebSocket webSocket = WebSocket.CreateFromStream(context.Connection.ConnectedStream, isServer:true, subProtocol, keepAliveInterval, buffer); + WebSocket webSocket = WebSocket.CreateFromStream(context.Connection.ConnectedStream, isServer:true, subProtocol, keepAliveInterval); HttpListenerWebSocketContext webSocketContext = new HttpListenerWebSocketContext( request.Url, diff --git a/src/System.Net.Mail/tests/Functional/SmtpClientTest.cs b/src/System.Net.Mail/tests/Functional/SmtpClientTest.cs index 618ebf85c7..60605123df 100644 --- a/src/System.Net.Mail/tests/Functional/SmtpClientTest.cs +++ b/src/System.Net.Mail/tests/Functional/SmtpClientTest.cs @@ -17,10 +17,9 @@ using Xunit; namespace System.Net.Mail.Tests { - public class SmtpClientTest : IDisposable + public class SmtpClientTest : FileCleanupTestBase { private SmtpClient _smtp; - private string _tempFolder; private SmtpClient Smtp { @@ -34,28 +33,17 @@ namespace System.Net.Mail.Tests { get { - if (_tempFolder == null) - { - _tempFolder = Path.Combine(Path.GetTempPath(), GetType().FullName, Guid.NewGuid().ToString()); - if (Directory.Exists(_tempFolder)) - Directory.Delete(_tempFolder, true); - - Directory.CreateDirectory(_tempFolder); - } - - return _tempFolder; + return TestDirectory; } } - public void Dispose() + protected override void Dispose(bool disposing) { if (_smtp != null) { _smtp.Dispose(); } - - if (Directory.Exists(_tempFolder)) - Directory.Delete(_tempFolder, true); + base.Dispose(disposing); } [Theory] diff --git a/src/System.Net.Primitives/src/System/Net/CookieContainer.cs b/src/System.Net.Primitives/src/System/Net/CookieContainer.cs index 27dc045528..527a94ec2d 100644 --- a/src/System.Net.Primitives/src/System/Net/CookieContainer.cs +++ b/src/System.Net.Primitives/src/System/Net/CookieContainer.cs @@ -97,6 +97,7 @@ namespace System.Net public const int DefaultPerDomainCookieLimit = 20; public const int DefaultCookieLengthLimit = 4096; + private static readonly string s_fqdnMyDomain = CreateFqdnMyDomain(); private static readonly HeaderVariantInfo[] s_headerInfo = { new HeaderVariantInfo(HttpKnownHeaderNames.SetCookie, CookieVariant.Rfc2109), new HeaderVariantInfo(HttpKnownHeaderNames.SetCookie2, CookieVariant.Rfc2965) @@ -107,19 +108,13 @@ namespace System.Net private int m_maxCookies = DefaultCookieLimit; // Do not rename (binary serialization) private int m_maxCookiesPerDomain = DefaultPerDomainCookieLimit; // Do not rename (binary serialization) private int m_count = 0; // Do not rename (binary serialization) - private string m_fqdnMyDomain = string.Empty; // Do not rename (binary serialization) + private string m_fqdnMyDomain = s_fqdnMyDomain; // Do not rename (binary serialization) public CookieContainer() { - string domain = HostInformation.DomainName; - if (domain != null && domain.Length > 1) - { - m_fqdnMyDomain = '.' + domain; - } - // Otherwise it will remain string.Empty. } - public CookieContainer(int capacity) : this() + public CookieContainer(int capacity) { if (capacity <= 0) { @@ -142,6 +137,14 @@ namespace System.Net m_maxCookieSize = maxCookieSize; } + private static string CreateFqdnMyDomain() + { + string domain = HostInformation.DomainName; + return domain != null && domain.Length > 1 ? + '.' + domain : + string.Empty; + } + // NOTE: after shrinking the capacity, Count can become greater than Capacity. public int Capacity { diff --git a/src/System.Net.Security/ref/System.Net.Security.cs b/src/System.Net.Security/ref/System.Net.Security.cs index fbe4022fbe..785eadd9c2 100644 --- a/src/System.Net.Security/ref/System.Net.Security.cs +++ b/src/System.Net.Security/ref/System.Net.Security.cs @@ -32,6 +32,8 @@ namespace System.Net.Security RequireEncryption = 0, } public delegate System.Security.Cryptography.X509Certificates.X509Certificate LocalCertificateSelectionCallback(object sender, string targetHost, System.Security.Cryptography.X509Certificates.X509CertificateCollection localCertificates, System.Security.Cryptography.X509Certificates.X509Certificate remoteCertificate, string[] acceptableIssuers); + public delegate System.Security.Cryptography.X509Certificates.X509Certificate ServerCertificateSelectionCallback(object sender, string hostName); + public partial class NegotiateStream : AuthenticatedStream { public NegotiateStream(System.IO.Stream innerStream) : base(innerStream, false) { } @@ -108,6 +110,7 @@ namespace System.Net.Security public X509RevocationMode CertificateRevocationCheckMode { get { throw null; } set { } } public List<SslApplicationProtocol> ApplicationProtocols { get { throw null; } set { } } public RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get { throw null; } set { } } + public ServerCertificateSelectionCallback ServerCertificateSelectionCallback { get { throw null; } set { } } public EncryptionPolicy EncryptionPolicy { get { throw null; } set { } } } public partial class SslClientAuthenticationOptions diff --git a/src/System.Net.Security/src/System.Net.Security.csproj b/src/System.Net.Security/src/System.Net.Security.csproj index 5600de5691..e9a5737afe 100644 --- a/src/System.Net.Security/src/System.Net.Security.csproj +++ b/src/System.Net.Security/src/System.Net.Security.csproj @@ -23,6 +23,7 @@ <Compile Include="System\Net\FixedSizeReader.cs" /> <Compile Include="System\Net\HelperAsyncResults.cs" /> <Compile Include="System\Net\Logging\NetEventSource.cs" /> + <Compile Include="System\Net\Security\SniHelper.cs" /> <Compile Include="System\Net\Security\SslApplicationProtocol.cs" /> <Compile Include="System\Net\Security\SslAuthenticationOptions.cs" /> <Compile Include="System\Net\Security\SslClientAuthenticationOptions.cs" /> diff --git a/src/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs b/src/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs index 1fc55e8997..913e7e590e 100644 --- a/src/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs +++ b/src/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs @@ -239,44 +239,81 @@ namespace System.Net } } + private static readonly SslProtocols[] s_orderedSslProtocols = new SslProtocols[5] + { +#pragma warning disable 0618 + SslProtocols.Ssl2, + SslProtocols.Ssl3, +#pragma warning restore 0618 + SslProtocols.Tls, + SslProtocols.Tls11, + SslProtocols.Tls12 + }; + private static void SetProtocols(SafeSslHandle sslContext, SslProtocols protocols) { - const SslProtocols SupportedProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12; - SslProtocols minProtocolId; - SslProtocols maxProtocolId; + // A contiguous range of protocols is required. Find the min and max of the range, + // or throw if it's non-contiguous or if no protocols are specified. - switch (protocols & SupportedProtocols) + // First, mark all of the specified protocols. + SslProtocols[] orderedSslProtocols = s_orderedSslProtocols; + Span<bool> protocolSet = stackalloc bool[orderedSslProtocols.Length]; + for (int i = 0; i < orderedSslProtocols.Length; i++) { - case SslProtocols.None: - throw new PlatformNotSupportedException(SR.net_securityprotocolnotsupported); - case SslProtocols.Tls: - minProtocolId = SslProtocols.Tls; - maxProtocolId = SslProtocols.Tls; - break; - case SslProtocols.Tls11: - minProtocolId = SslProtocols.Tls11; - maxProtocolId = SslProtocols.Tls11; - break; - case SslProtocols.Tls12: - minProtocolId = SslProtocols.Tls12; - maxProtocolId = SslProtocols.Tls12; - break; - case SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12: - minProtocolId = SslProtocols.Tls; - maxProtocolId = SslProtocols.Tls12; - break; - case SslProtocols.Tls11 | SslProtocols.Tls12: - minProtocolId = SslProtocols.Tls11; - maxProtocolId = SslProtocols.Tls12; - break; - case SslProtocols.Tls | SslProtocols.Tls11: - minProtocolId = SslProtocols.Tls; - maxProtocolId = SslProtocols.Tls11; + protocolSet[i] = (protocols & orderedSslProtocols[i]) != 0; + } + + SslProtocols minProtocolId = (SslProtocols)(-1); + SslProtocols maxProtocolId = (SslProtocols)(-1); + + // Loop through them, starting from the lowest. + for (int min = 0; min < protocolSet.Length; min++) + { + if (protocolSet[min]) + { + // We found the first one that's set; that's the bottom of the range. + minProtocolId = orderedSslProtocols[min]; + + // Now loop from there to look for the max of the range. + for (int max = min + 1; max < protocolSet.Length; max++) + { + if (!protocolSet[max]) + { + // We found the first one after the min that's not set; the top of the range + // is the one before this (which might be the same as the min). + maxProtocolId = orderedSslProtocols[max - 1]; + + // Finally, verify that nothing beyond this one is set, as that would be + // a discontiguous set of protocols. + for (int verifyNotSet = max + 1; verifyNotSet < protocolSet.Length; verifyNotSet++) + { + if (protocolSet[verifyNotSet]) + { + throw new PlatformNotSupportedException(SR.Format(SR.net_security_sslprotocol_contiguous, protocols)); + } + } + + break; + } + } + break; - default: - throw new PlatformNotSupportedException(SR.Format(SR.net_security_sslprotocol_contiguous, protocols)); + } + } + + // If no protocols were set, throw. + if (minProtocolId == (SslProtocols)(-1)) + { + throw new PlatformNotSupportedException(SR.net_securityprotocolnotsupported); + } + + // If we didn't find an unset protocol after the min, go all the way to the last one. + if (maxProtocolId == (SslProtocols)(-1)) + { + maxProtocolId = orderedSslProtocols[orderedSslProtocols.Length - 1]; } + // Finally set this min and max. Interop.AppleCrypto.SslSetMinProtocolVersion(sslContext, minProtocolId); Interop.AppleCrypto.SslSetMaxProtocolVersion(sslContext, maxProtocolId); } diff --git a/src/System.Net.Security/src/System/Net/Security/SecureChannel.cs b/src/System.Net.Security/src/System/Net/Security/SecureChannel.cs index 4c92ae8754..6440b38c6a 100644 --- a/src/System.Net.Security/src/System/Net/Security/SecureChannel.cs +++ b/src/System.Net.Security/src/System/Net/Security/SecureChannel.cs @@ -624,7 +624,7 @@ namespace System.Net.Security // // Acquire Server Side Certificate information and set it on the class. // - private bool AcquireServerCredentials(ref byte[] thumbPrint) + private bool AcquireServerCredentials(ref byte[] thumbPrint, byte[] clientHello) { if (NetEventSource.IsEnabled) NetEventSource.Enter(this); @@ -632,7 +632,18 @@ namespace System.Net.Security X509Certificate localCertificate = null; bool cachedCred = false; - if (_sslAuthenticationOptions.CertSelectionDelegate != null) + if (_sslAuthenticationOptions.ServerCertSelectionDelegate != null) + { + string serverIdentity = SniHelper.GetServerName(clientHello); + localCertificate = _sslAuthenticationOptions.ServerCertSelectionDelegate(serverIdentity); + + if (localCertificate == null) + { + throw new AuthenticationException(SR.net_ssl_io_no_server_cert); + } + } + // This probably never gets called as this is a client options delegate + else if (_sslAuthenticationOptions.CertSelectionDelegate != null) { X509CertificateCollection tempCollection = new X509CertificateCollection(); tempCollection.Add(_sslAuthenticationOptions.ServerCertificate); @@ -744,7 +755,6 @@ namespace System.Net.Security #if TRACE_VERBOSE if (NetEventSource.IsEnabled) NetEventSource.Enter(this, $"_refreshCredentialNeeded = {_refreshCredentialNeeded}"); #endif - if (offset < 0 || offset > (input == null ? 0 : input.Length)) { NetEventSource.Fail(this, "Argument 'offset' out of range."); @@ -786,7 +796,7 @@ namespace System.Net.Security if (_refreshCredentialNeeded) { cachedCreds = _sslAuthenticationOptions.IsServer - ? AcquireServerCredentials(ref thumbPrint) + ? AcquireServerCredentials(ref thumbPrint, input) : AcquireClientCredentials(ref thumbPrint); } diff --git a/src/System.Net.Security/src/System/Net/Security/SniHelper.cs b/src/System.Net.Security/src/System/Net/Security/SniHelper.cs new file mode 100644 index 0000000000..b1540f122b --- /dev/null +++ b/src/System.Net.Security/src/System/Net/Security/SniHelper.cs @@ -0,0 +1,391 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers.Binary; +using System.Globalization; +using System.Text; + +namespace System.Net.Security +{ + internal class SniHelper + { + private const int ProtocolVersionSize = 2; + private const int UInt24Size = 3; + private const int RandomSize = 32; + private readonly static IdnMapping s_idnMapping = CreateIdnMapping(); + private readonly static Encoding s_encoding = CreateEncoding(); + + public static string GetServerName(byte[] clientHello) + { + return GetSniFromSslPlainText(clientHello); + } + + private static string GetSniFromSslPlainText(ReadOnlySpan<byte> sslPlainText) + { + // https://tools.ietf.org/html/rfc6101#section-5.2.1 + // struct { + // ContentType type; // enum with max value 255 + // ProtocolVersion version; // 2x uint8 + // uint16 length; + // opaque fragment[SSLPlaintext.length]; + // } SSLPlaintext; + const int ContentTypeOffset = 0; + const int ProtocolVersionOffset = ContentTypeOffset + sizeof(ContentType); + const int LengthOffset = ProtocolVersionOffset + ProtocolVersionSize; + const int HandshakeOffset = LengthOffset + sizeof(ushort); + + // SSL v2's ContentType has 0x80 bit set. + // We do not care about SSL v2 here because it does not support client hello extensions + if (sslPlainText.Length < HandshakeOffset || (ContentType)sslPlainText[ContentTypeOffset] != ContentType.Handshake) + { + return null; + } + + // Skip ContentType and ProtocolVersion + int handshakeLength = BinaryPrimitives.ReadUInt16BigEndian(sslPlainText.Slice(LengthOffset)); + ReadOnlySpan<byte> sslHandshake = sslPlainText.Slice(HandshakeOffset); + + if (handshakeLength != sslHandshake.Length) + { + return null; + } + + return GetSniFromSslHandshake(sslHandshake); + } + + private static string GetSniFromSslHandshake(ReadOnlySpan<byte> sslHandshake) + { + // https://tools.ietf.org/html/rfc6101#section-5.6 + // struct { + // HandshakeType msg_type; /* handshake type */ + // uint24 length; /* bytes in message */ + // select (HandshakeType) { + // ... + // case client_hello: ClientHello; + // ... + // } body; + // } Handshake; + const int HandshakeTypeOffset = 0; + const int ClientHelloLengthOffset = HandshakeTypeOffset + sizeof(HandshakeType); + const int ClientHelloOffset = ClientHelloLengthOffset + UInt24Size; + + if (sslHandshake.Length < ClientHelloOffset || (HandshakeType)sslHandshake[HandshakeTypeOffset] != HandshakeType.ClientHello) + { + return null; + } + + int clientHelloLength = ReadUInt24BigEndian(sslHandshake.Slice(ClientHelloLengthOffset)); + ReadOnlySpan<byte> clientHello = sslHandshake.Slice(ClientHelloOffset); + + if (clientHello.Length != clientHelloLength) + { + return null; + } + + return GetSniFromClientHello(clientHello); + } + + private static string GetSniFromClientHello(ReadOnlySpan<byte> clientHello) + { + // Basic structure: https://tools.ietf.org/html/rfc6101#section-5.6.1.2 + // Extended structure: https://tools.ietf.org/html/rfc3546#section-2.1 + // struct { + // ProtocolVersion client_version; // 2x uint8 + // Random random; // 32 bytes + // SessionID session_id; // opaque type + // CipherSuite cipher_suites<2..2^16-1>; // opaque type + // CompressionMethod compression_methods<1..2^8-1>; // opaque type + // Extension client_hello_extension_list<0..2^16-1>; + // } ClientHello; + ReadOnlySpan<byte> p = SkipBytes(clientHello, ProtocolVersionSize + RandomSize); + + // Skip SessionID (max size 32 => size fits in 1 byte) + p = SkipOpaqueType1(p); + + // Skip cipher suites (max size 2^16-1 => size fits in 2 bytes) + p = SkipOpaqueType2(p, out _); + + // Skip compression methods (max size 2^8-1 => size fits in 1 byte) + p = SkipOpaqueType1(p); + + // is invalid structure or no extensions? + if (p.IsEmpty) + { + return null; + } + + // client_hello_extension_list (max size 2^16-1 => size fits in 2 bytes) + int extensionListLength = BinaryPrimitives.ReadUInt16BigEndian(p); + p = SkipBytes(p, sizeof(ushort)); + + if (extensionListLength != p.Length) + { + return null; + } + + string ret = null; + while (!p.IsEmpty) + { + bool invalid; + string sni = GetSniFromExtension(p, out p, out invalid); + if (invalid) + { + return null; + } + + if (ret != null && sni != null) + { + return null; + } + + if (sni != null) + { + ret = sni; + } + } + + return ret; + } + + private static string GetSniFromExtension(ReadOnlySpan<byte> extension, out ReadOnlySpan<byte> remainingBytes, out bool invalid) + { + // https://tools.ietf.org/html/rfc3546#section-2.3 + // struct { + // ExtensionType extension_type; + // opaque extension_data<0..2^16-1>; + // } Extension; + const int ExtensionDataOffset = sizeof(ExtensionType); + + if (extension.Length < ExtensionDataOffset) + { + remainingBytes = ReadOnlySpan<byte>.Empty; + invalid = true; + return null; + } + + ExtensionType extensionType = (ExtensionType)BinaryPrimitives.ReadUInt16BigEndian(extension); + ReadOnlySpan<byte> extensionData = extension.Slice(ExtensionDataOffset); + + if (extensionType == ExtensionType.ServerName) + { + return GetSniFromServerNameList(extensionData, out remainingBytes, out invalid); + } + else + { + remainingBytes = SkipOpaqueType2(extensionData, out invalid); + return null; + } + } + + private static string GetSniFromServerNameList(ReadOnlySpan<byte> serverNameListExtension, out ReadOnlySpan<byte> remainingBytes, out bool invalid) + { + // https://tools.ietf.org/html/rfc3546#section-3.1 + // struct { + // ServerName server_name_list<1..2^16-1> + // } ServerNameList; + // ServerNameList is an opaque type (length of sufficient size for max data length is prepended) + const int ServerNameListOffset = sizeof(ushort); + + if (serverNameListExtension.Length < ServerNameListOffset) + { + remainingBytes = ReadOnlySpan<byte>.Empty; + invalid = true; + return null; + } + + int serverNameListLength = BinaryPrimitives.ReadUInt16BigEndian(serverNameListExtension); + ReadOnlySpan<byte> serverNameList = serverNameListExtension.Slice(ServerNameListOffset); + + if (serverNameListLength > serverNameList.Length) + { + remainingBytes = ReadOnlySpan<byte>.Empty; + invalid = true; + return null; + } + + remainingBytes = serverNameList.Slice(serverNameListLength); + ReadOnlySpan<byte> serverName = serverNameList.Slice(0, serverNameListLength); + + return GetSniFromServerName(serverName, out invalid); + } + + private static string GetSniFromServerName(ReadOnlySpan<byte> serverName, out bool invalid) + { + // https://tools.ietf.org/html/rfc3546#section-3.1 + // struct { + // NameType name_type; + // select (name_type) { + // case host_name: HostName; + // } name; + // } ServerName; + // ServerName is an opaque type (length of sufficient size for max data length is prepended) + const int ServerNameLengthOffset = 0; + const int NameTypeOffset = ServerNameLengthOffset + sizeof(ushort); + const int HostNameStructOffset = NameTypeOffset + sizeof(NameType); + + if (serverName.Length < HostNameStructOffset) + { + invalid = true; + return null; + } + + // Following can underflow but it is ok due to equality check below + int hostNameStructLength = BinaryPrimitives.ReadUInt16BigEndian(serverName) - sizeof(NameType); + NameType nameType = (NameType)serverName[NameTypeOffset]; + ReadOnlySpan<byte> hostNameStruct = serverName.Slice(HostNameStructOffset); + + if (hostNameStructLength != hostNameStruct.Length || nameType != NameType.HostName) + { + invalid = true; + return null; + } + + return GetSniFromHostNameStruct(hostNameStruct, out invalid); + } + + private static string GetSniFromHostNameStruct(ReadOnlySpan<byte> hostNameStruct, out bool invalid) + { + // https://tools.ietf.org/html/rfc3546#section-3.1 + // HostName is an opaque type (length of sufficient size for max data length is prepended) + const int HostNameLengthOffset = 0; + const int HostNameOffset = HostNameLengthOffset + sizeof(ushort); + + int hostNameLength = BinaryPrimitives.ReadUInt16BigEndian(hostNameStruct); + ReadOnlySpan<byte> hostName = hostNameStruct.Slice(HostNameOffset); + if (hostNameLength != hostName.Length) + { + invalid = true; + return null; + } + + invalid = false; + return DecodeString(hostName); + } + + private static string DecodeString(ReadOnlySpan<byte> bytes) + { + // https://tools.ietf.org/html/rfc3546#section-3.1 + // Per spec: + // If the hostname labels contain only US-ASCII characters, then the + // client MUST ensure that labels are separated only by the byte 0x2E, + // representing the dot character U+002E (requirement 1 in section 3.1 + // of [IDNA] notwithstanding). If the server needs to match the HostName + // against names that contain non-US-ASCII characters, it MUST perform + // the conversion operation described in section 4 of [IDNA], treating + // the HostName as a "query string" (i.e. the AllowUnassigned flag MUST + // be set). Note that IDNA allows labels to be separated by any of the + // Unicode characters U+002E, U+3002, U+FF0E, and U+FF61, therefore + // servers MUST accept any of these characters as a label separator. If + // the server only needs to match the HostName against names containing + // exclusively ASCII characters, it MUST compare ASCII names case- + // insensitively. + + string idnEncodedString; + try + { + idnEncodedString = s_encoding.GetString(bytes); + } + catch (DecoderFallbackException) + { + return null; + } + + try + { + return s_idnMapping.GetUnicode(idnEncodedString); + } + catch (ArgumentException) + { + // client has not done IDN mapping + return idnEncodedString; + } + } + + private static int ReadUInt24BigEndian(ReadOnlySpan<byte> bytes) + { + return (bytes[0] << 16) | (bytes[1] << 8) | bytes[2]; + } + + private static ReadOnlySpan<byte> SkipBytes(ReadOnlySpan<byte> bytes, int numberOfBytesToSkip) + { + return (numberOfBytesToSkip < bytes.Length) ? bytes.Slice(numberOfBytesToSkip) : ReadOnlySpan<byte>.Empty; + } + + // Opaque type is of structure: + // - length (minimum number of bytes to hold the max value) + // - data (length bytes) + // We will only use opaque types which are of max size: 255 (length = 1) or 2^16-1 (length = 2). + // We will call them SkipOpaqueType`length` + private static ReadOnlySpan<byte> SkipOpaqueType1(ReadOnlySpan<byte> bytes) + { + const int OpaqueTypeLengthSize = sizeof(byte); + if (bytes.Length < OpaqueTypeLengthSize) + { + return ReadOnlySpan<byte>.Empty; + } + + byte length = bytes[0]; + int totalBytes = OpaqueTypeLengthSize + length; + + return SkipBytes(bytes, totalBytes); + } + + private static ReadOnlySpan<byte> SkipOpaqueType2(ReadOnlySpan<byte> bytes, out bool invalid) + { + const int OpaqueTypeLengthSize = sizeof(ushort); + if (bytes.Length < OpaqueTypeLengthSize) + { + invalid = true; + return ReadOnlySpan<byte>.Empty; + } + + ushort length = BinaryPrimitives.ReadUInt16BigEndian(bytes); + int totalBytes = OpaqueTypeLengthSize + length; + + invalid = bytes.Length < totalBytes; + if (invalid) + { + return ReadOnlySpan<byte>.Empty; + } + else + { + return bytes.Slice(totalBytes); + } + } + + private static IdnMapping CreateIdnMapping() + { + return new IdnMapping() + { + // Per spec "AllowUnassigned flag MUST be set". See comment above GetSniFromServerNameList for more details. + AllowUnassigned = true + }; + } + + private static Encoding CreateEncoding() + { + return Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback()); + } + + private enum ContentType : byte + { + Handshake = 0x16 + } + + private enum HandshakeType : byte + { + ClientHello = 0x01 + } + + private enum ExtensionType : ushort + { + ServerName = 0x00 + } + + private enum NameType : byte + { + HostName = 0x00 + } + } +} diff --git a/src/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs b/src/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs index 5fede486d1..73741757fd 100644 --- a/src/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs +++ b/src/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs @@ -37,7 +37,6 @@ namespace System.Net.Security // Common options. AllowRenegotiation = sslServerAuthenticationOptions.AllowRenegotiation; ApplicationProtocols = sslServerAuthenticationOptions.ApplicationProtocols; - CertValidationDelegate = sslServerAuthenticationOptions._certValidationDelegate; CheckCertName = false; EnabledSslProtocols = sslServerAuthenticationOptions.EnabledSslProtocols; EncryptionPolicy = sslServerAuthenticationOptions.EncryptionPolicy; @@ -66,6 +65,7 @@ namespace System.Net.Security internal bool CheckCertName { get; set; } internal RemoteCertValidationCallback CertValidationDelegate { get; set; } internal LocalCertSelectionCallback CertSelectionDelegate { get; set; } + internal ServerCertSelectionCallback ServerCertSelectionDelegate { get; set; } } } diff --git a/src/System.Net.Security/src/System/Net/Security/SslServerAuthenticationOptions.cs b/src/System.Net.Security/src/System/Net/Security/SslServerAuthenticationOptions.cs index cf5b271a10..5b3935ba07 100644 --- a/src/System.Net.Security/src/System/Net/Security/SslServerAuthenticationOptions.cs +++ b/src/System.Net.Security/src/System/Net/Security/SslServerAuthenticationOptions.cs @@ -15,8 +15,6 @@ namespace System.Net.Security private EncryptionPolicy _encryptionPolicy = EncryptionPolicy.RequireEncryption; private bool _allowRenegotiation = true; - internal RemoteCertValidationCallback _certValidationDelegate; - public bool AllowRenegotiation { get => _allowRenegotiation; @@ -29,6 +27,8 @@ namespace System.Net.Security public RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; set; } + public ServerCertificateSelectionCallback ServerCertificateSelectionCallback { get; set; } + public X509Certificate ServerCertificate { get; set; } public SslProtocols EnabledSslProtocols diff --git a/src/System.Net.Security/src/System/Net/Security/SslState.cs b/src/System.Net.Security/src/System/Net/Security/SslState.cs index 0e08a47427..9c60b706cc 100644 --- a/src/System.Net.Security/src/System/Net/Security/SslState.cs +++ b/src/System.Net.Security/src/System/Net/Security/SslState.cs @@ -132,7 +132,7 @@ namespace System.Net.Security } } - internal void ValidateCreateContext(SslServerAuthenticationOptions sslServerAuthenticationOptions) + internal void ValidateCreateContext(SslAuthenticationOptions sslAuthenticationOptions) { ThrowIfExceptional(); @@ -146,15 +146,11 @@ namespace System.Net.Security throw new InvalidOperationException(SR.net_auth_client_server); } - if (sslServerAuthenticationOptions.ServerCertificate == null) - { - throw new ArgumentNullException(nameof(sslServerAuthenticationOptions.ServerCertificate)); - } - _exception = null; + _sslAuthenticationOptions = sslAuthenticationOptions; + try { - _sslAuthenticationOptions = new SslAuthenticationOptions(sslServerAuthenticationOptions); _context = new SecureChannel(_sslAuthenticationOptions); } catch (Win32Exception e) diff --git a/src/System.Net.Security/src/System/Net/Security/SslStream.cs b/src/System.Net.Security/src/System/Net/Security/SslStream.cs index a4ff6d8c05..941a064ead 100644 --- a/src/System.Net.Security/src/System/Net/Security/SslStream.cs +++ b/src/System.Net.Security/src/System/Net/Security/SslStream.cs @@ -31,9 +31,12 @@ namespace System.Net.Security // A user delegate used to select local SSL certificate. public delegate X509Certificate LocalCertificateSelectionCallback(object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers); + public delegate X509Certificate ServerCertificateSelectionCallback(object sender, string hostName); + // Internal versions of the above delegates. internal delegate bool RemoteCertValidationCallback(string host, X509Certificate2 certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors); internal delegate X509Certificate LocalCertSelectionCallback(string targetHost, X509CertificateCollection localCertificates, X509Certificate2 remoteCertificate, string[] acceptableIssuers); + internal delegate X509Certificate ServerCertSelectionCallback(string hostName); public class SslStream : AuthenticatedStream { @@ -42,6 +45,7 @@ namespace System.Net.Security internal RemoteCertificateValidationCallback _userCertificateValidationCallback; internal LocalCertificateSelectionCallback _userCertificateSelectionCallback; + internal ServerCertificateSelectionCallback _userServerCertificateSelectionCallback; internal RemoteCertValidationCallback _certValidationDelegate; internal LocalCertSelectionCallback _certSelectionDelegate; internal EncryptionPolicy _encryptionPolicy; @@ -141,6 +145,33 @@ namespace System.Net.Security return _userCertificateSelectionCallback(this, targetHost, localCertificates, remoteCertificate, acceptableIssuers); } + private X509Certificate ServerCertSelectionCallbackWrapper(string targetHost) + { + return _userServerCertificateSelectionCallback(this, targetHost); + } + + private SslAuthenticationOptions CreateAuthenticationOptions(SslServerAuthenticationOptions sslServerAuthenticationOptions) + { + if (sslServerAuthenticationOptions.ServerCertificate == null && sslServerAuthenticationOptions.ServerCertificateSelectionCallback == null) + { + throw new ArgumentNullException(nameof(sslServerAuthenticationOptions.ServerCertificate)); + } + + if (sslServerAuthenticationOptions.ServerCertificate != null && sslServerAuthenticationOptions.ServerCertificateSelectionCallback != null) + { + throw new InvalidOperationException(SR.Format(SR.net_conflicting_options, nameof(ServerCertificateSelectionCallback))); + } + + var authOptions = new SslAuthenticationOptions(sslServerAuthenticationOptions); + + _userServerCertificateSelectionCallback = sslServerAuthenticationOptions.ServerCertificateSelectionCallback; + authOptions.ServerCertSelectionDelegate = _userServerCertificateSelectionCallback == null ? null : new ServerCertSelectionCallback(ServerCertSelectionCallbackWrapper); + + authOptions.CertValidationDelegate = _certValidationDelegate; + + return authOptions; + } + // // Client side auth. // @@ -174,7 +205,6 @@ namespace System.Net.Security internal virtual IAsyncResult BeginAuthenticateAsClient(SslClientAuthenticationOptions sslClientAuthenticationOptions, CancellationToken cancellationToken, AsyncCallback asyncCallback, object asyncState) { - SecurityProtocol.ThrowOnNotAllowed(sslClientAuthenticationOptions.EnabledSslProtocols); SetAndVerifyValidationCallback(sslClientAuthenticationOptions.RemoteCertificateValidationCallback); SetAndVerifySelectionCallback(sslClientAuthenticationOptions.LocalCertificateSelectionCallback); @@ -226,13 +256,9 @@ namespace System.Net.Security private IAsyncResult BeginAuthenticateAsServer(SslServerAuthenticationOptions sslServerAuthenticationOptions, CancellationToken cancellationToken, AsyncCallback asyncCallback, object asyncState) { - SecurityProtocol.ThrowOnNotAllowed(sslServerAuthenticationOptions.EnabledSslProtocols); SetAndVerifyValidationCallback(sslServerAuthenticationOptions.RemoteCertificateValidationCallback); - // Set the delegate on the options. - sslServerAuthenticationOptions._certValidationDelegate = _certValidationDelegate; - - _sslState.ValidateCreateContext(sslServerAuthenticationOptions); + _sslState.ValidateCreateContext(CreateAuthenticationOptions(sslServerAuthenticationOptions)); LazyAsyncResult result = new LazyAsyncResult(_sslState, asyncState, asyncCallback); _sslState.ProcessAuthentication(result); @@ -294,7 +320,6 @@ namespace System.Net.Security private void AuthenticateAsClient(SslClientAuthenticationOptions sslClientAuthenticationOptions) { - SecurityProtocol.ThrowOnNotAllowed(sslClientAuthenticationOptions.EnabledSslProtocols); SetAndVerifyValidationCallback(sslClientAuthenticationOptions.RemoteCertificateValidationCallback); SetAndVerifySelectionCallback(sslClientAuthenticationOptions.LocalCertificateSelectionCallback); @@ -328,13 +353,9 @@ namespace System.Net.Security private void AuthenticateAsServer(SslServerAuthenticationOptions sslServerAuthenticationOptions) { - SecurityProtocol.ThrowOnNotAllowed(sslServerAuthenticationOptions.EnabledSslProtocols); SetAndVerifyValidationCallback(sslServerAuthenticationOptions.RemoteCertificateValidationCallback); - // Set the delegate on the options. - sslServerAuthenticationOptions._certValidationDelegate = _certValidationDelegate; - - _sslState.ValidateCreateContext(sslServerAuthenticationOptions); + _sslState.ValidateCreateContext(CreateAuthenticationOptions(sslServerAuthenticationOptions)); _sslState.ProcessAuthentication(null); } #endregion diff --git a/src/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs b/src/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs index 399d1c3750..1217c710fe 100644 --- a/src/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs +++ b/src/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs @@ -467,9 +467,8 @@ namespace System.Net.Security return SSPIWrapper.AcquireCredentialsHandle(GlobalSSPI.SSPISecureChannel, SecurityPackage, credUsage, secureCredential); }); } - catch (Exception ex) + catch { - Debug.Fail("AcquireCredentialsHandle failed.", ex.ToString()); return SSPIWrapper.AcquireCredentialsHandle(GlobalSSPI.SSPISecureChannel, SecurityPackage, credUsage, secureCredential); } } diff --git a/src/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs b/src/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs index 4739156715..d11df7b8af 100644 --- a/src/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs +++ b/src/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs @@ -55,15 +55,18 @@ namespace System.Net.Security.Tests await ClientAsyncSslHelper(protocol, protocol); } - [Theory] - [ClassData(typeof(SslProtocolSupport.UnsupportedSslProtocolsTestData))] - [ActiveIssue(16534, TestPlatforms.Windows)] - public async Task ClientAsyncAuthenticate_EachClientUnsupportedProtocol_Fail(SslProtocols protocol) + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public async Task ClientAsyncAuthenticate_Ssl2WithSelf_Success() { - await Assert.ThrowsAsync<NotSupportedException>(() => + // Test Ssl2 against itself. This is a standalone test as even on versions where Windows supports Ssl2, + // it appears to have rules around not using it when other protocols are mentioned. + if (!PlatformDetection.IsWindows10Version1607OrGreater) { - return ClientAsyncSslHelper(protocol, SslProtocolSupport.SupportedSslProtocols); - }); +#pragma warning disable 0618 + await ClientAsyncSslHelper(SslProtocols.Ssl2, SslProtocols.Ssl2); +#pragma warning restore 0618 + } } [Theory] @@ -74,7 +77,9 @@ namespace System.Net.Security.Tests SslProtocols clientProtocol, Type expectedException) { - await Assert.ThrowsAsync(expectedException, () => ClientAsyncSslHelper(serverProtocol, clientProtocol)); + Exception e = await Record.ExceptionAsync(() => ClientAsyncSslHelper(serverProtocol, clientProtocol)); + Assert.NotNull(e); + Assert.IsAssignableFrom(expectedException, e); } [Fact] @@ -86,18 +91,6 @@ namespace System.Net.Security.Tests SslProtocolSupport.SupportedSslProtocols); } - [Fact] - [ActiveIssue(16534, TestPlatforms.Windows)] - public async Task ClientAsyncAuthenticate_UnsupportedAllClient_Fail() - { - await Assert.ThrowsAsync<NotSupportedException>(() => - { - return ClientAsyncSslHelper( - SslProtocolSupport.UnsupportedSslProtocols, - SslProtocolSupport.SupportedSslProtocols); - }); - } - [Theory] [ClassData(typeof(SslProtocolSupport.SupportedSslProtocolsTestData))] [ActiveIssue(16534, TestPlatforms.Windows)] @@ -120,6 +113,11 @@ namespace System.Net.Security.Tests private static IEnumerable<object[]> ProtocolMismatchData() { +#pragma warning disable 0618 + yield return new object[] { SslProtocols.Ssl2, SslProtocols.Ssl3, typeof(Exception) }; + yield return new object[] { SslProtocols.Ssl2, SslProtocols.Tls12, typeof(Exception) }; + yield return new object[] { SslProtocols.Ssl3, SslProtocols.Tls12, typeof(Exception) }; +#pragma warning restore 0618 yield return new object[] { SslProtocols.Tls, SslProtocols.Tls11, typeof(IOException) }; yield return new object[] { SslProtocols.Tls, SslProtocols.Tls12, typeof(IOException) }; yield return new object[] { SslProtocols.Tls11, SslProtocols.Tls, typeof(AuthenticationException) }; diff --git a/src/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs b/src/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs index fd01c39fbb..c91a0775c5 100644 --- a/src/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs +++ b/src/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs @@ -3,9 +3,9 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.ComponentModel; using System.Net.Sockets; using System.Net.Test.Common; -using System.Runtime.ExceptionServices; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; @@ -42,20 +42,6 @@ namespace System.Net.Security.Tests await ServerAsyncSslHelper(protocol, protocol); } - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "These protocols are explicitly disabled on .NET Core but still supported on .NET Framework.")] - [Theory] - [ClassData(typeof(SslProtocolSupport.UnsupportedSslProtocolsTestData))] - public async Task ServerAsyncAuthenticate_EachServerUnsupportedProtocol_Fail(SslProtocols protocol) - { - await Assert.ThrowsAsync<NotSupportedException>(() => - { - return ServerAsyncSslHelper( - SslProtocolSupport.SupportedSslProtocols, - protocol, - expectedToFail: true); - }); - } - [Theory] [MemberData(nameof(ProtocolMismatchData))] public async Task ServerAsyncAuthenticate_MismatchProtocols_Fails( @@ -63,8 +49,7 @@ namespace System.Net.Security.Tests SslProtocols clientProtocol, Type expectedException) { - await Assert.ThrowsAsync( - expectedException, + Exception e = await Record.ExceptionAsync( () => { return ServerAsyncSslHelper( @@ -72,19 +57,9 @@ namespace System.Net.Security.Tests clientProtocol, expectedToFail: true); }); - } - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "These protocols are explicitly disabled on .NET Core but still supported on .NET Framework.")] - [Fact] - public async Task ServerAsyncAuthenticate_UnsupportedAllServer_Fail() - { - await Assert.ThrowsAsync<NotSupportedException>(() => - { - return ServerAsyncSslHelper( - SslProtocolSupport.SupportedSslProtocols, - SslProtocolSupport.UnsupportedSslProtocols, - expectedToFail: true); - }); + Assert.NotNull(e); + Assert.IsAssignableFrom(expectedException, e); } [Theory] @@ -97,6 +72,11 @@ namespace System.Net.Security.Tests private static IEnumerable<object[]> ProtocolMismatchData() { +#pragma warning disable 0618 + yield return new object[] { SslProtocols.Ssl2, SslProtocols.Ssl3, typeof(Exception) }; + yield return new object[] { SslProtocols.Ssl2, SslProtocols.Tls12, typeof(Exception) }; + yield return new object[] { SslProtocols.Ssl3, SslProtocols.Tls12, typeof(Exception) }; +#pragma warning restore 0618 yield return new object[] { SslProtocols.Tls, SslProtocols.Tls11, typeof(AuthenticationException) }; yield return new object[] { SslProtocols.Tls, SslProtocols.Tls12, typeof(AuthenticationException) }; yield return new object[] { SslProtocols.Tls11, SslProtocols.Tls, typeof(TimeoutException) }; diff --git a/src/System.Net.Security/tests/FunctionalTests/SniHelperTest.cs b/src/System.Net.Security/tests/FunctionalTests/SniHelperTest.cs new file mode 100644 index 0000000000..3dafcc083f --- /dev/null +++ b/src/System.Net.Security/tests/FunctionalTests/SniHelperTest.cs @@ -0,0 +1,3255 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace System.Net.Security.Tests +{ + public class SniHelperTest + { + [Fact] + public void SniHelper_ValidData_Ok() + { + InvalidClientHello(s_validClientHello, -1, shouldPass: true); + } + + [Theory] + [MemberData(nameof(InvalidClientHelloData))] + public void SniHelper_InvalidData_Fails(int id, byte[] clientHello) + { + InvalidClientHello(clientHello, id, shouldPass: false); + } + + [Theory] + [MemberData(nameof(InvalidClientHelloDataTruncatedBytes))] + public void SniHelper_TruncatedData_Fails(int id, byte[] clientHello) + { + InvalidClientHello(clientHello, id, shouldPass: false); + } + + private void InvalidClientHello(byte[] clientHello, int id, bool shouldPass) + { + string ret = SniHelper.GetServerName(clientHello); + if (shouldPass) + Assert.NotNull(ret); + else + Assert.Null(ret); + } + + private static IEnumerable<object[]> InvalidClientHelloData() + { + int id = 0; + foreach (byte[] invalidClientHello in InvalidClientHello()) + { + id++; + yield return new object[] { id, invalidClientHello }; + } + } + + private static IEnumerable<object[]> InvalidClientHelloDataTruncatedBytes() + { + // converting to base64 first to remove duplicated test cases + var uniqueInvalidHellos = new HashSet<string>(); + foreach (byte[] invalidClientHello in InvalidClientHello()) + { + for (int i = 0; i < invalidClientHello.Length - 1; i++) + { + uniqueInvalidHellos.Add(Convert.ToBase64String(invalidClientHello.Take(i).ToArray())); + } + } + + for (int i = 0; i < s_validClientHello.Length - 1; i++) + { + uniqueInvalidHellos.Add(Convert.ToBase64String(s_validClientHello.Take(i).ToArray())); + } + + int id = 0; + foreach (string invalidClientHello in uniqueInvalidHellos) + { + id++; + yield return new object[] { id, Convert.FromBase64String(invalidClientHello) }; + } + } + + private static byte[] s_validClientHello = new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x0C, 0x3C, 0x85, 0x78, 0xCA, + 0x67, 0x70, 0xAA, 0x38, 0xCB, + 0x28, 0xBC, 0xDC, 0x3E, 0x30, + 0xBF, 0x11, 0x96, 0x95, 0x1A, + 0xB9, 0xF0, 0x99, 0xA4, 0x91, + 0x09, 0x13, 0xB4, 0x89, 0x94, + 0x27, 0x2E, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + private static IEnumerable<byte[]> InvalidClientHello() + { + // This test covers following test cases: + // - Length of structure off by 1 (search for "length off by 1") + // - Length of structure is max length (search for "max length") + // - Type is invalid or unknown (i.e. SslPlainText.ClientType is not 0x16 - search for "unknown") + // - Invalid utf-8 characters + // in each case sni will be null or will cause parsing error - we only expect some parsing errors, + // anything else is considered a bug + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 - length off by 1 + 0x00, 0x02, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 - max length + 0xFF, 0xFF, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 - length off by 1 + 0x00, 0x01, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 - max length + 0xFF, 0xFF, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 - length off by 1 + 0x00, 0x01, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 - max length + 0xFF, 0xFF, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D - length off by 1 + 0x00, 0x15, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D - max length + 0xFF, 0xFF, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B - length off by 1 + 0x00, 0x03, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B - max length + 0xFF, 0xFF, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A - length off by 1 + 0x00, 0x09, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A - max length + 0xFF, 0xFF, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length - length off by 1 + 0x00, 0x35, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length - max length + 0xFF, 0xFF, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type - unknown + 0x01, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length - length off by 1 + 0x00, 0x38, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length - max length + 0xFF, 0xFF, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length - length off by 1 + 0x00, 0x3A, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length - max length + 0xFF, 0xFF, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) - unknown + 0x01, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length - length off by 1 + 0x00, 0x75, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length - max length + 0xFF, 0xFF, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods - length off by 1 + 0x02, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods - max length + 0xFF, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites - length off by 1 + 0x00, 0x2B, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites - max length + 0xFF, 0xFF, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId - length off by 1 + 0x01, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId - max length + 0xFF, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length - length off by 1 + 0x00, 0x00, 0xC8, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length - max length + 0xFF, 0xFF, 0xFF, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) - unknown + 0x00, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length - length off by 1 + 0x00, 0xCC, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length - max length + 0xFF, 0xFF, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) - unknown + 0x01, 0x03, 0x04, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x58, 0xAA, 0x5F, 0xE7, 0x22, + 0xCF, 0x9F, 0x59, 0x8A, 0xC5, + 0x8B, 0x87, 0xC7, 0x62, 0x32, + 0x98, 0xD4, 0xD8, 0xA2, 0xBE, + 0x77, 0xCE, 0xA9, 0xCE, 0x42, + 0x25, 0x5A, 0x8B, 0xEE, 0x16, + 0x80, 0xF1, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + + yield return new byte[] { + // SslPlainText.(ContentType+ProtocolVersion) + 0x16, 0x03, 0x03, + // SslPlainText.length + 0x00, 0xCB, + // Handshake.msg_type (client hello) + 0x01, + // Handshake.length + 0x00, 0x00, 0xC7, + // ClientHello.client_version + 0x03, 0x03, + // ClientHello.random + 0x0C, 0x3C, 0x85, 0x78, 0xCA, + 0x67, 0x70, 0xAA, 0x38, 0xCB, + 0x28, 0xBC, 0xDC, 0x3E, 0x30, + 0xBF, 0x11, 0x96, 0x95, 0x1A, + 0xB9, 0xF0, 0x99, 0xA4, 0x91, + 0x09, 0x13, 0xB4, 0x89, 0x94, + 0x27, 0x2E, + // ClientHello.SessionId + 0x00, + // ClientHello.cipher_suites + 0x00, 0x2A, 0xC0, 0x2C, 0xC0, + 0x2B, 0xC0, 0x30, 0xC0, 0x2F, + 0x00, 0x9F, 0x00, 0x9E, 0xC0, + 0x24, 0xC0, 0x23, 0xC0, 0x28, + 0xC0, 0x27, 0xC0, 0x0A, 0xC0, + 0x09, 0xC0, 0x14, 0xC0, 0x13, + 0x00, 0x9D, 0x00, 0x9C, 0x00, + 0x3D, 0x00, 0x3C, 0x00, 0x35, + 0x00, 0x2F, 0x00, 0x0A, + // ClientHello.compression_methods + 0x01, 0x01, + // ClientHello.extension_list_length + 0x00, 0x74, + // Extension.extension_type (server_name) + 0x00, 0x00, + // ServerNameListExtension.length + 0x00, 0x39, + // ServerName.length + 0x00, 0x37, + // ServerName.type + 0x00, + // HostName.length + 0x00, 0x34, + // HostName.bytes + 0x80, 0x80, 0x80, 0x80, 0x61, // 0x80 0x80 0x80 0x80 is a forbidden utf-8 sequence + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, + // Extension.extension_type (00 0A) + 0x00, 0x0A, + // Extension 0A + 0x00, 0x08, 0x00, 0x06, 0x00, + 0x1D, 0x00, 0x17, 0x00, 0x18, + // Extension.extension_type (00 0B) + 0x00, 0x0B, + // Extension 0B + 0x00, 0x02, 0x01, 0x00, + // Extension.extension_type (00 0D) + 0x00, 0x0D, + // Extension 0D + 0x00, 0x14, 0x00, 0x12, 0x04, + 0x01, 0x05, 0x01, 0x02, 0x01, + 0x04, 0x03, 0x05, 0x03, 0x02, + 0x03, 0x02, 0x02, 0x06, 0x01, + 0x06, 0x03, + // Extension.extension_type (00 23) + 0x00, 0x23, + // Extension 00 23 + 0x00, 0x00, + // Extension.extension_type (00 17) + 0x00, 0x17, + // Extension 17 + 0x00, 0x00, + // Extension.extension_type (FF 01) + 0xFF, 0x01, + // Extension FF01 + 0x00, 0x01, 0x00 + }; + } + } +} diff --git a/src/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs b/src/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs new file mode 100644 index 0000000000..55cf3e9483 --- /dev/null +++ b/src/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs @@ -0,0 +1,135 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Net.Test.Common; +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace System.Net.Security.Tests +{ + using Configuration = System.Net.Test.Common.Configuration; + + public class SslStreamSniTest + { + [Theory] + [MemberData(nameof(HostNameData))] + public void SslStream_ClientSendsSNIServerReceives_Ok(string hostName) + { + X509Certificate serverCert = Configuration.Certificates.GetSelfSignedServerCertificate(); + + WithVirtualConnection((server, client) => + { + Task clientJob = Task.Run(() => { + client.AuthenticateAsClient(hostName); + }); + + SslServerAuthenticationOptions options = DefaultServerOptions(); + + int timesCallbackCalled = 0; + options.ServerCertificateSelectionCallback = (sender, actualHostName) => + { + timesCallbackCalled++; + Assert.Equal(hostName, actualHostName); + return serverCert; + }; + + var cts = new CancellationTokenSource(); + server.AuthenticateAsServerAsync(options, cts.Token).Wait(); + + Assert.Equal(1, timesCallbackCalled); + clientJob.Wait(); + }, + (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) => + { + Assert.Equal(serverCert, certificate); + return true; + }); + } + + [Fact] + public void SslStream_NoSniFromClient_CallbackReturnsNull() + { + WithVirtualConnection((server, client) => + { + Task clientJob = Task.Run(() => { + Assert.Throws<VirtualNetwork.VirtualNetworkConnectionBroken>(() + => client.AuthenticateAsClient("test")); + }); + + int timesCallbackCalled = 0; + SslServerAuthenticationOptions options = DefaultServerOptions(); + options.ServerCertificateSelectionCallback = (sender, actualHostName) => + { + timesCallbackCalled++; + return null; + }; + + var cts = new CancellationTokenSource(); + Assert.Throws<AuthenticationException>(WithAggregateExceptionUnwrapping(() => + server.AuthenticateAsServerAsync(options, cts.Token).Wait() + )); + + // to break connection so that client is not waiting + server.Dispose(); + + Assert.Equal(1, timesCallbackCalled); + + clientJob.Wait(); + }, + (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) => + { + return true; + }); + } + + private static Action WithAggregateExceptionUnwrapping(Action a) + { + return () => { + try + { + a(); + } + catch (AggregateException e) + { + throw e.InnerException; + } + }; + } + + private static SslServerAuthenticationOptions DefaultServerOptions() + { + return new SslServerAuthenticationOptions() + { + ClientCertificateRequired = false, + EnabledSslProtocols = SslProtocols.Tls, + CertificateRevocationCheckMode = X509RevocationMode.NoCheck, + }; + } + + private void WithVirtualConnection(Action<SslStream, SslStream> serverClientConnection, RemoteCertificateValidationCallback clientCertValidate) + { + VirtualNetwork vn = new VirtualNetwork(); + using (VirtualNetworkStream serverStream = new VirtualNetworkStream(vn, isServer: true), + clientStream = new VirtualNetworkStream(vn, isServer: false)) + using (SslStream server = new SslStream(serverStream, leaveInnerStreamOpen: false), + client = new SslStream(clientStream, leaveInnerStreamOpen: false, clientCertValidate)) + { + serverClientConnection(server, client); + } + } + + private static IEnumerable<object[]> HostNameData() + { + yield return new object[] { "a" }; + yield return new object[] { "test" }; + // max allowed hostname length is 63 + yield return new object[] { new string('a', 63) }; + yield return new object[] { "\u017C\u00F3\u0142\u0107 g\u0119\u015Bl\u0105 ja\u017A\u0144. \u7EA2\u70E7. \u7167\u308A\u713C\u304D" }; + } + } +} diff --git a/src/System.Net.Security/tests/FunctionalTests/SslStreamStreamToStreamTest.cs b/src/System.Net.Security/tests/FunctionalTests/SslStreamStreamToStreamTest.cs index ae608d302b..61da0d226e 100644 --- a/src/System.Net.Security/tests/FunctionalTests/SslStreamStreamToStreamTest.cs +++ b/src/System.Net.Security/tests/FunctionalTests/SslStreamStreamToStreamTest.cs @@ -310,7 +310,11 @@ namespace System.Net.Security.Tests [Fact] public async Task SslStream_StreamToStream_Dispose_Throws() { - VirtualNetwork network = new VirtualNetwork(); + VirtualNetwork network = new VirtualNetwork() + { + DisableConnectionBreaking = true + }; + using (var clientStream = new VirtualNetworkStream(network, isServer: false)) using (var serverStream = new VirtualNetworkStream(network, isServer: true)) using (var clientSslStream = new SslStream(clientStream, false, AllowAnyServerCertificate)) diff --git a/src/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs b/src/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs index 941d34fce4..311ea12e21 100644 --- a/src/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs +++ b/src/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs @@ -45,37 +45,56 @@ namespace System.Net.Security.Tests [InlineData(SslProtocols.Tls12, null)] [InlineData(SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12, null)] [InlineData(null, SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12)] +#pragma warning disable 0618 + [InlineData(SslProtocols.Default, null)] + [InlineData(null, SslProtocols.Default)] +#pragma warning restore 0618 public async Task ClientAndServer_OneOrBothUseDefault_Ok(SslProtocols? clientProtocols, SslProtocols? serverProtocols) { - const int SEC_E_BUFFER_TOO_SMALL = unchecked((int)0x80090321); - - X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate(); - string serverHost = serverCertificate.GetNameInfo(X509NameType.SimpleName, false); - var clientCertificates = new X509CertificateCollection(); - clientCertificates.Add(Configuration.Certificates.GetClientCertificate()); - - var tasks = new Task[2]; - tasks[0] = AuthenticateClientAsync(serverHost, clientCertificates, checkCertificateRevocation: false, protocols: clientProtocols); - tasks[1] = AuthenticateServerAsync(serverCertificate, clientCertificateRequired: true, checkCertificateRevocation: false, protocols: serverProtocols); - - try + using (X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate()) + using (X509Certificate2 clientCertificate = Configuration.Certificates.GetClientCertificate()) { - await TestConfiguration.WhenAllOrAnyFailedWithTimeout(tasks); - if (PlatformDetection.IsWindows && PlatformDetection.WindowsVersion >= 10) + string serverHost = serverCertificate.GetNameInfo(X509NameType.SimpleName, false); + var clientCertificates = new X509CertificateCollection() { clientCertificate }; + + await TestConfiguration.WhenAllOrAnyFailedWithTimeout( + AuthenticateClientAsync(serverHost, clientCertificates, checkCertificateRevocation: false, protocols: clientProtocols), + AuthenticateServerAsync(serverCertificate, clientCertificateRequired: true, checkCertificateRevocation: false, protocols: serverProtocols)); + if (PlatformDetection.IsWindows && PlatformDetection.WindowsVersion >= 10 && +#pragma warning disable 0618 + clientProtocols.GetValueOrDefault() != SslProtocols.Default && + serverProtocols.GetValueOrDefault() != SslProtocols.Default) +#pragma warning restore 0618 { Assert.True( (_clientStream.SslProtocol == SslProtocols.Tls11 && _clientStream.HashAlgorithm == HashAlgorithmType.Sha1) || _clientStream.HashAlgorithm == HashAlgorithmType.Sha256 || _clientStream.HashAlgorithm == HashAlgorithmType.Sha384 || - _clientStream.HashAlgorithm == HashAlgorithmType.Sha512); + _clientStream.HashAlgorithm == HashAlgorithmType.Sha512, + _clientStream.SslProtocol + " " + _clientStream.HashAlgorithm); } } - catch (HttpRequestException e) when (e.InnerException?.GetType().Name == "WinHttpException" && - e.InnerException.HResult == SEC_E_BUFFER_TOO_SMALL && - !PlatformDetection.IsWindows10Version1607OrGreater) + } + + [ConditionalTheory(nameof(IsNotWindows7))] +#pragma warning disable 0618 + [InlineData(null, SslProtocols.Ssl2)] + [InlineData(SslProtocols.None, SslProtocols.Ssl2)] + [InlineData(SslProtocols.Ssl2, null)] + [InlineData(SslProtocols.Ssl2, SslProtocols.None)] +#pragma warning restore 0618 + public async Task ClientAndServer_OneUsesDefault_OtherUsesLowerProtocol_Fails( + SslProtocols? clientProtocols, SslProtocols? serverProtocols) + { + using (X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate()) + using (X509Certificate2 clientCertificate = Configuration.Certificates.GetClientCertificate()) { - // Testing on old Windows versions can hit https://github.com/dotnet/corefx/issues/7812 - // Ignore SEC_E_BUFFER_TOO_SMALL error on such cases. + string serverHost = serverCertificate.GetNameInfo(X509NameType.SimpleName, false); + var clientCertificates = new X509CertificateCollection() { clientCertificate }; + + await Assert.ThrowsAnyAsync<Exception>(() => TestConfiguration.WhenAllOrAnyFailedWithTimeout( + AuthenticateClientAsync(serverHost, clientCertificates, checkCertificateRevocation: false, protocols: clientProtocols), + AuthenticateServerAsync(serverCertificate, clientCertificateRequired: true, checkCertificateRevocation: false, protocols: serverProtocols))); } } diff --git a/src/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj b/src/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj index 8268fd077b..c758241a3b 100644 --- a/src/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj +++ b/src/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj @@ -90,10 +90,15 @@ </ItemGroup> <ItemGroup Condition="'$(TargetGroup)'=='netcoreapp'"> <!-- TODO #13070: Add net463 to the condition after the TFM gets updated to the actual .Net 4.7--> + <Compile Include="..\..\src\System\Net\Security\SniHelper.cs"> + <Link>src\SniHelper.cs</Link> + </Compile> + <Compile Include="SniHelperTest.cs" /> <Compile Include="SslAuthenticationOptionsTest.cs" /> <Compile Include="SslStreamAlertsTest.cs" /> <Compile Include="SslStreamAllowRenegotiationTests.cs" /> <Compile Include="SslStreamAlpnTests.cs" /> + <Compile Include="SslStreamSniTest.cs" /> <Compile Include="SslStreamEKUTest.cs" /> <Compile Include="SslStreamSchSendAuxRecordTest.cs" /> <Compile Include="SslStreamCredentialCacheTest.cs" /> @@ -159,4 +164,4 @@ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> </ItemGroup> <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> -</Project>
\ No newline at end of file +</Project> diff --git a/src/System.Net.Security/tests/UnitTests/Fakes/FakeSslState.cs b/src/System.Net.Security/tests/UnitTests/Fakes/FakeSslState.cs index 9382bd1398..52d650a8a0 100644 --- a/src/System.Net.Security/tests/UnitTests/Fakes/FakeSslState.cs +++ b/src/System.Net.Security/tests/UnitTests/Fakes/FakeSslState.cs @@ -25,7 +25,7 @@ namespace System.Net.Security { } - internal void ValidateCreateContext(SslServerAuthenticationOptions sslServerAuthenticationOptions) + internal void ValidateCreateContext(SslAuthenticationOptions sslAuthenticationOptions) { } diff --git a/src/System.Net.Security/tests/UnitTests/SslStreamAllowedProtocolsTest.cs b/src/System.Net.Security/tests/UnitTests/SslStreamAllowedProtocolsTest.cs index b316e129e4..a2229efde9 100644 --- a/src/System.Net.Security/tests/UnitTests/SslStreamAllowedProtocolsTest.cs +++ b/src/System.Net.Security/tests/UnitTests/SslStreamAllowedProtocolsTest.cs @@ -19,14 +19,6 @@ namespace System.Net.Security.Tests string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation); [Theory] - [ClassData(typeof(SslProtocolSupport.UnsupportedSslProtocolsTestData))] - public void SslStream_AuthenticateAsClientAsync_NotSupported_Fails(SslProtocols protocol) - { - SslStream stream = new SslStream(new NotImplementedStream()); - Assert.Throws<NotSupportedException>(() => AuthenticateAsClient(stream, false, "host", null, protocol, false)); - } - - [Theory] [ClassData(typeof(SslProtocolSupport.SupportedSslProtocolsTestData))] public void SslStream_AuthenticateAsClientAsync_Supported_Success(SslProtocols protocol) { @@ -43,27 +35,6 @@ namespace System.Net.Security.Tests } [Fact] - public void SslStream_AuthenticateAsClientAsync_Invalid_Fails() - { - SslStream stream = new SslStream(new NotImplementedStream()); - Assert.Throws<NotSupportedException>(() => AuthenticateAsClient(stream, false, "host", null, (SslProtocols)4096, false)); - } - - [Fact] - public void SslStream_AuthenticateAsClient_Invalid_Fails() - { - SslStream stream = new SslStream(new NotImplementedStream()); - Assert.Throws<NotSupportedException>(() => AuthenticateAsClient(stream, false, "host", null, (SslProtocols)4096, false)); - } - - [Fact] - public void SslStream_AuthenticateAsClientAsync_AllUnsupported_Fails() - { - SslStream stream = new SslStream(new NotImplementedStream()); - Assert.Throws<NotSupportedException>(() => AuthenticateAsClient(stream, false, "host", null, SslProtocolSupport.UnsupportedSslProtocols, false)); - } - - [Fact] public void SslStream_AuthenticateAsClientAsync_AllSupported_Success() { SslStream stream = new SslStream(new NotImplementedStream()); diff --git a/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs b/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs index 6320ac5d40..ef7a0a0ce6 100644 --- a/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs +++ b/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs @@ -266,7 +266,7 @@ namespace System.Net.Sockets // we can't pool the object, as ProcessQueue may still have a reference to it, due to // using a pattern whereby it takes the lock to grab an item, but then releases the lock // to do further processing on the item that's still in the list. - ThreadPool.QueueUserWorkItem(o => ((AsyncOperation)o).InvokeCallback(allowPooling: false), this); + ThreadPool.UnsafeQueueUserWorkItem(o => ((AsyncOperation)o).InvokeCallback(allowPooling: false), this); } Trace("Exit"); @@ -837,7 +837,7 @@ namespace System.Net.Sockets // We just transitioned from Waiting to Processing. // Spawn a work item to do the actual processing. - ThreadPool.QueueUserWorkItem(s_processingCallback, context); + ThreadPool.UnsafeQueueUserWorkItem(s_processingCallback, context); } // Called on the threadpool when data may be available. @@ -968,7 +968,7 @@ namespace System.Net.Sockets Debug.Assert(_state == QueueState.Processing); // Spawn a new work item to continue processing the queue. - ThreadPool.QueueUserWorkItem(s_processingCallback, context); + ThreadPool.UnsafeQueueUserWorkItem(s_processingCallback, context); } // At this point, the operation has completed and it's no longer diff --git a/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs b/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs index 39b503551e..b270ff7c77 100644 --- a/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs +++ b/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs @@ -286,11 +286,21 @@ namespace System.Net.Sockets // // Start the event loop on its own thread. // - Task.Factory.StartNew( - EventLoop, - CancellationToken.None, - TaskCreationOptions.LongRunning, - TaskScheduler.Default); + bool suppressFlow = !ExecutionContext.IsFlowSuppressed(); + try + { + if (suppressFlow) ExecutionContext.SuppressFlow(); + Task.Factory.StartNew( + s => ((SocketAsyncEngine)s).EventLoop(), + this, + CancellationToken.None, + TaskCreationOptions.LongRunning, + TaskScheduler.Default); + } + finally + { + if (suppressFlow) ExecutionContext.RestoreFlow(); + } } catch { diff --git a/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs b/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs index 0caf582202..ad12b6c911 100644 --- a/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs +++ b/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs @@ -1053,7 +1053,6 @@ namespace System.Net.Sockets try { Debug.Assert(_singleBufferHandleState == SingleBufferHandleState.Set); - Debug.Assert(_singleBufferHandle.HasPointer); bool userBuffer = _count >= _acceptAddressBufferCount; _currentSocket.GetAcceptExSockaddrs( diff --git a/src/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.netcoreapp.cs b/src/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.netcoreapp.cs index 78f1545426..5fb9e97ecf 100644 --- a/src/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.netcoreapp.cs +++ b/src/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.netcoreapp.cs @@ -182,7 +182,7 @@ namespace System.Net.Sockets.Tests [Fact] public void SetBufferMemory_NonArray_BufferReturnsNull() { - using (var m = new NativeOwnedMemory(42)) + using (var m = new NativeMemoryManager(42)) using (var saea = new SocketAsyncEventArgs()) { saea.SetBuffer(m.Memory); diff --git a/src/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.netcoreapp.cs b/src/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.netcoreapp.cs index 3e41627e37..945e905764 100644 --- a/src/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.netcoreapp.cs +++ b/src/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.netcoreapp.cs @@ -39,7 +39,7 @@ namespace System.Net.Sockets.Tests public override bool ValidatesArrayArguments => false; public override async Task<int> ReceiveAsync(Socket s, ArraySegment<byte> buffer) { - using (var m = new NativeOwnedMemory(buffer.Count)) + using (var m = new NativeMemoryManager(buffer.Count)) { int bytesReceived = await s.ReceiveAsync(m.Memory, SocketFlags.None).ConfigureAwait(false); m.Memory.Span.Slice(0, bytesReceived).CopyTo(buffer.AsSpan()); @@ -48,7 +48,7 @@ namespace System.Net.Sockets.Tests } public override async Task<int> SendAsync(Socket s, ArraySegment<byte> buffer) { - using (var m = new NativeOwnedMemory(buffer.Count)) + using (var m = new NativeMemoryManager(buffer.Count)) { buffer.AsSpan().CopyTo(m.Memory.Span); return await s.SendAsync(m.Memory, SocketFlags.None).ConfigureAwait(false); diff --git a/src/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj b/src/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj index 186d721e72..8ef8f9b4cd 100644 --- a/src/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj +++ b/src/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj @@ -98,8 +98,8 @@ <Compile Include="$(CommonTestPath)\System\Threading\Tasks\TaskTimeoutExtensions.cs"> <Link>Common\System\Threading\Tasks\TaskTimeoutExtensions.cs</Link> </Compile> - <Compile Include="$(CommonTestPath)\System\Buffers\NativeOwnedMemory.cs"> - <Link>Common\System\Buffers\NativeOwnedMemory.cs</Link> + <Compile Include="$(CommonTestPath)\System\Buffers\NativeMemoryManager.cs"> + <Link>Common\System\Buffers\NativeMemoryManager.cs</Link> </Compile> <Compile Include="$(CommonPath)\System\Threading\Tasks\TaskToApm.cs"> <Link>Common\System\Threading\Tasks\TaskToApm.cs</Link> diff --git a/src/System.Net.WebSockets.WebSocketProtocol/ref/System.Net.WebSockets.WebSocketProtocol.cs b/src/System.Net.WebSockets.WebSocketProtocol/ref/System.Net.WebSockets.WebSocketProtocol.cs index b66dd7b603..87509360b6 100644 --- a/src/System.Net.WebSockets.WebSocketProtocol/ref/System.Net.WebSockets.WebSocketProtocol.cs +++ b/src/System.Net.WebSockets.WebSocketProtocol/ref/System.Net.WebSockets.WebSocketProtocol.cs @@ -5,12 +5,10 @@ // Changes to this file must follow the http://aka.ms/api-review process. // ------------------------------------------------------------------------------ -using System.IO; - namespace System.Net.WebSockets { public static class WebSocketProtocol { - public static WebSocket CreateFromStream(Stream stream, bool isServer, string subProtocol, TimeSpan keepAliveInterval, Memory<byte> buffer = default) { throw null; } + public static WebSocket CreateFromStream(System.IO.Stream stream, bool isServer, string subProtocol, System.TimeSpan keepAliveInterval) { throw null; } } } diff --git a/src/System.Net.WebSockets.WebSocketProtocol/src/System/Net/WebSockets/WebSocketProtocol.cs b/src/System.Net.WebSockets.WebSocketProtocol/src/System/Net/WebSockets/WebSocketProtocol.cs index e4756eb603..732cbb5e91 100644 --- a/src/System.Net.WebSockets.WebSocketProtocol/src/System/Net/WebSockets/WebSocketProtocol.cs +++ b/src/System.Net.WebSockets.WebSocketProtocol/src/System/Net/WebSockets/WebSocketProtocol.cs @@ -13,8 +13,7 @@ namespace System.Net.WebSockets Stream stream, bool isServer, string subProtocol, - TimeSpan keepAliveInterval, - Memory<byte> buffer = default) + TimeSpan keepAliveInterval) { if (stream == null) { @@ -38,7 +37,7 @@ namespace System.Net.WebSockets 0)); } - return ManagedWebSocket.CreateFromConnectedStream(stream, isServer, subProtocol, keepAliveInterval, buffer); + return ManagedWebSocket.CreateFromConnectedStream(stream, isServer, subProtocol, keepAliveInterval); } } } diff --git a/src/System.Net.WebSockets.WebSocketProtocol/tests/WebSocketProtocolTests.cs b/src/System.Net.WebSockets.WebSocketProtocol/tests/WebSocketProtocolTests.cs index 9db60cd882..b5a5c2857f 100644 --- a/src/System.Net.WebSockets.WebSocketProtocol/tests/WebSocketProtocolTests.cs +++ b/src/System.Net.WebSockets.WebSocketProtocol/tests/WebSocketProtocolTests.cs @@ -25,19 +25,19 @@ namespace System.Net.WebSockets.Tests public void CreateFromStream_InvalidArguments_Throws() { AssertExtensions.Throws<ArgumentNullException>("stream", - () => WebSocketProtocol.CreateFromStream(null, true, "subProtocol", TimeSpan.FromSeconds(30), default(Memory<byte>))); + () => WebSocketProtocol.CreateFromStream(null, true, "subProtocol", TimeSpan.FromSeconds(30))); AssertExtensions.Throws<ArgumentException>("stream", - () => WebSocketProtocol.CreateFromStream(new MemoryStream(new byte[100], writable: false), true, "subProtocol", TimeSpan.FromSeconds(30), default(Memory<byte>))); + () => WebSocketProtocol.CreateFromStream(new MemoryStream(new byte[100], writable: false), true, "subProtocol", TimeSpan.FromSeconds(30))); AssertExtensions.Throws<ArgumentException>("stream", - () => WebSocketProtocol.CreateFromStream(new UnreadableStream(), true, "subProtocol", TimeSpan.FromSeconds(30), default(Memory<byte>))); + () => WebSocketProtocol.CreateFromStream(new UnreadableStream(), true, "subProtocol", TimeSpan.FromSeconds(30))); AssertExtensions.Throws<ArgumentException>("subProtocol", - () => WebSocketProtocol.CreateFromStream(new MemoryStream(), true, " ", TimeSpan.FromSeconds(30), default(Memory<byte>))); + () => WebSocketProtocol.CreateFromStream(new MemoryStream(), true, " ", TimeSpan.FromSeconds(30))); AssertExtensions.Throws<ArgumentException>("subProtocol", - () => WebSocketProtocol.CreateFromStream(new MemoryStream(), true, "\xFF", TimeSpan.FromSeconds(30), default(Memory<byte>))); + () => WebSocketProtocol.CreateFromStream(new MemoryStream(), true, "\xFF", TimeSpan.FromSeconds(30))); AssertExtensions.Throws<ArgumentOutOfRangeException>("keepAliveInterval", () => - WebSocketProtocol.CreateFromStream(new MemoryStream(), true, "subProtocol", TimeSpan.FromSeconds(-2), default(Memory<byte>))); + WebSocketProtocol.CreateFromStream(new MemoryStream(), true, "subProtocol", TimeSpan.FromSeconds(-2))); } [Theory] @@ -47,8 +47,8 @@ namespace System.Net.WebSockets.Tests [InlineData(4096)] public void CreateFromStream_ValidBufferSizes_Succeed(int bufferSize) { - Assert.NotNull(WebSocketProtocol.CreateFromStream(new MemoryStream(), false, null, Timeout.InfiniteTimeSpan, new Memory<byte>(new byte[bufferSize]))); - Assert.NotNull(WebSocketProtocol.CreateFromStream(new MemoryStream(), true, null, Timeout.InfiniteTimeSpan, new Memory<byte>(new byte[bufferSize]))); + Assert.NotNull(WebSocketProtocol.CreateFromStream(new MemoryStream(), false, null, Timeout.InfiniteTimeSpan)); + Assert.NotNull(WebSocketProtocol.CreateFromStream(new MemoryStream(), true, null, Timeout.InfiniteTimeSpan)); } [OuterLoop] // Connects to external server. diff --git a/src/System.Net.WebSockets/ref/System.Net.WebSockets.cs b/src/System.Net.WebSockets/ref/System.Net.WebSockets.cs index 126887bc2e..c174799633 100644 --- a/src/System.Net.WebSockets/ref/System.Net.WebSockets.cs +++ b/src/System.Net.WebSockets/ref/System.Net.WebSockets.cs @@ -29,7 +29,7 @@ namespace System.Net.WebSockets public static System.ArraySegment<byte> CreateClientBuffer(int receiveBufferSize, int sendBufferSize) { throw null; } [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] public static System.Net.WebSockets.WebSocket CreateClientWebSocket(System.IO.Stream innerStream, string subProtocol, int receiveBufferSize, int sendBufferSize, System.TimeSpan keepAliveInterval, bool useZeroMaskingKey, System.ArraySegment<byte> internalBuffer) { throw null; } - public static System.Net.WebSockets.WebSocket CreateFromStream(System.IO.Stream stream, bool isServer, string subProtocol, System.TimeSpan keepAliveInterval, System.Memory<byte> buffer=default(System.Memory<byte>)) { throw null; } + public static System.Net.WebSockets.WebSocket CreateFromStream(System.IO.Stream stream, bool isServer, string subProtocol, System.TimeSpan keepAliveInterval) { throw null; } public static System.ArraySegment<byte> CreateServerBuffer(int receiveBufferSize) { throw null; } public abstract void Dispose(); [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] diff --git a/src/System.Net.WebSockets/src/System/Net/WebSockets/WebSocket.cs b/src/System.Net.WebSockets/src/System/Net/WebSockets/WebSocket.cs index 64a8192275..27ae87c6a8 100644 --- a/src/System.Net.WebSockets/src/System/Net/WebSockets/WebSocket.cs +++ b/src/System.Net.WebSockets/src/System/Net/WebSockets/WebSocket.cs @@ -135,9 +135,8 @@ namespace System.Net.WebSockets /// <param name="isServer"><code>true</code> if this is the server-side of the connection; <code>false</code> if it's the client side.</param> /// <param name="subProtocol">The agreed upon sub-protocol that was used when creating the connection.</param> /// <param name="keepAliveInterval">The keep-alive interval to use, or <see cref="Timeout.InfiniteTimeSpan"/> to disable keep-alives.</param> - /// <param name="buffer">A scratch buffer that may be used by the implementation for any purpose.</param> /// <returns>The created <see cref="WebSocket"/>.</returns> - public static WebSocket CreateFromStream(Stream stream, bool isServer, string subProtocol, TimeSpan keepAliveInterval, Memory<byte> buffer = default) + public static WebSocket CreateFromStream(Stream stream, bool isServer, string subProtocol, TimeSpan keepAliveInterval) { if (stream == null) { @@ -161,7 +160,7 @@ namespace System.Net.WebSockets 0)); } - return ManagedWebSocket.CreateFromConnectedStream(stream, isServer, subProtocol, keepAliveInterval, buffer); + return ManagedWebSocket.CreateFromConnectedStream(stream, isServer, subProtocol, keepAliveInterval); } [EditorBrowsable(EditorBrowsableState.Never)] @@ -211,12 +210,10 @@ namespace System.Net.WebSockets SR.Format(SR.net_WebSockets_ArgumentOutOfRange_TooSmall, 0)); } - Memory<byte> internalMemoryBuffer = - internalBuffer.Count >= receiveBufferSize ? internalBuffer : - receiveBufferSize >= ManagedWebSocket.MaxMessageHeaderLength ? new byte[receiveBufferSize] : - Memory<byte>.Empty; // let ManagedWebSocket create it + // Ignore useZeroMaskingKey. ManagedWebSocket doesn't currently support that debugging option. + // Ignore internalBuffer. ManagedWebSocket uses its own small buffer for headers/control messages. - return ManagedWebSocket.CreateFromConnectedStream(innerStream, false, subProtocol, keepAliveInterval, internalMemoryBuffer); + return ManagedWebSocket.CreateFromConnectedStream(innerStream, false, subProtocol, keepAliveInterval); } } } diff --git a/src/System.Net.WebSockets/tests/WebSocketTests.netcoreapp.cs b/src/System.Net.WebSockets/tests/WebSocketTests.netcoreapp.cs index a95bb7e319..59e1e2e049 100644 --- a/src/System.Net.WebSockets/tests/WebSocketTests.netcoreapp.cs +++ b/src/System.Net.WebSockets/tests/WebSocketTests.netcoreapp.cs @@ -29,17 +29,6 @@ namespace System.Net.WebSockets.Tests WebSocket.CreateFromStream(new MemoryStream(), true, "subProtocol", TimeSpan.FromSeconds(-2))); } - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(14)] - [InlineData(4096)] - public void CreateFromStream_ValidBufferSizes_Succeed(int bufferSize) - { - Assert.NotNull(WebSocket.CreateFromStream(new MemoryStream(), false, null, Timeout.InfiniteTimeSpan, new byte[bufferSize])); - Assert.NotNull(WebSocket.CreateFromStream(new MemoryStream(), true, null, Timeout.InfiniteTimeSpan, new byte[bufferSize])); - } - [Fact] public void ValueWebSocketReceiveResult_Ctor_InvalidArguments_Throws() { diff --git a/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs b/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs index 1670ceada6..27d9bdb824 100644 --- a/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs +++ b/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs @@ -92,16 +92,6 @@ namespace System.Runtime.Serialization Initialize(type, knownTypes, maxItemsInObjectGraph, ignoreExtensionDataObject, preserveObjectReferences, null, false); } - public DataContractSerializer(Type type, XmlDictionaryString rootName, XmlDictionaryString rootNamespace, - IEnumerable<Type> knownTypes, - int maxItemsInObjectGraph, - bool ignoreExtensionDataObject, - bool preserveObjectReferences, - DataContractResolver dataContractResolver) - { - Initialize(type, rootName, rootNamespace, knownTypes, maxItemsInObjectGraph, ignoreExtensionDataObject, preserveObjectReferences, /*dataContractSurrogate,*/ dataContractResolver, false); - } - public DataContractSerializer(Type type, DataContractSerializerSettings settings) { if (settings == null) diff --git a/src/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs b/src/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs index 88fc758431..5c81a051bd 100644 --- a/src/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs +++ b/src/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs @@ -693,7 +693,7 @@ namespace System.Xml.Serialization { if (special.TypeDesc.Kind == TypeKind.Node) { - value = Document.CreateTextNode(ReadString()); + value = Document.CreateTextNode(Reader.ReadString()); } else { @@ -706,11 +706,11 @@ namespace System.Xml.Serialization { if (text.Mapping.TypeDesc.CollapseWhitespace) { - value = CollapseWhitespace(ReadString()); + value = CollapseWhitespace(Reader.ReadString()); } else { - value = ReadString(); + value = Reader.ReadString(); } } else @@ -721,7 +721,7 @@ namespace System.Xml.Serialization } else { - value = WritePrimitive(text.Mapping, (state) => ((ReflectionXmlSerializationReader)state).ReadString(), this); + value = WritePrimitive(text.Mapping, (state) => ((ReflectionXmlSerializationReader)state).Reader.ReadString(), this); } } } @@ -1832,7 +1832,7 @@ namespace System.Xml.Serialization Func<object, string> functor = (state) => { var reader = (ReflectionXmlSerializationReader)state; - return reader.CollapseWhitespace(reader.ReadString()); + return reader.CollapseWhitespace(reader.Reader.ReadString()); }; o = WriteEnumMethod(enumMapping, functor, this); ReadEndElement(); diff --git a/src/System.Private.Xml/src/System/Xml/Serialization/XmlSchemaImporter.cs b/src/System.Private.Xml/src/System/Xml/Serialization/XmlSchemaImporter.cs index 6d4dc7c57e..b3d3af2471 100644 --- a/src/System.Private.Xml/src/System/Xml/Serialization/XmlSchemaImporter.cs +++ b/src/System.Private.Xml/src/System/Xml/Serialization/XmlSchemaImporter.cs @@ -37,19 +37,6 @@ namespace System.Xml.Serialization /// </devdoc> public XmlSchemaImporter(XmlSchemas schemas, CodeIdentifiers typeIdentifiers) : base(schemas, CodeGenerationOptions.GenerateProperties, new ImportContext(typeIdentifiers, false)) { } - /// <include file='doc\XmlSchemaImporter.uex' path='docs/doc[@for="XmlSchemaImporter.XmlSchemaImporter2"]/*' /> - /// <devdoc> - /// <para>[To be supplied.]</para> - /// </devdoc> - public XmlSchemaImporter(XmlSchemas schemas, CodeIdentifiers typeIdentifiers, CodeGenerationOptions options) : base(schemas, options, new ImportContext(typeIdentifiers, false)) { } - - /// <include file='doc\XmlSchemaImporter.uex' path='docs/doc[@for="XmlSchemaImporter.XmlSchemaImporter3"]/*' /> - /// <devdoc> - /// <para>[To be supplied.]</para> - /// </devdoc> - public XmlSchemaImporter(XmlSchemas schemas, CodeGenerationOptions options, ImportContext context) : base(schemas, options, context) { } - - /// <include file='doc\XmlSchemaImporter.uex' path='docs/doc[@for="XmlSchemaImporter.ImportDerivedTypeMapping"]/*' /> /// <devdoc> /// <para>[To be supplied.]</para> diff --git a/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs b/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs index 17a11a2cf5..3e349e4340 100644 --- a/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs +++ b/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs @@ -1428,40 +1428,6 @@ namespace System.Xml.Serialization Array.Copy(a, b, length); return b; } - // This is copied from Core's XmlReader.ReadString, as it is not exposed in the Contract. - protected virtual string ReadString() - { - if (Reader.ReadState != ReadState.Interactive) - { - return string.Empty; - } - Reader.MoveToElement(); - if (Reader.NodeType == XmlNodeType.Element) - { - if (Reader.IsEmptyElement) - { - return string.Empty; - } - else if (!Reader.Read()) - { - throw new InvalidOperationException(SR.Xml_InvalidOperation); - } - if (Reader.NodeType == XmlNodeType.EndElement) - { - return string.Empty; - } - } - string result = string.Empty; - while (IsTextualNode(Reader.NodeType)) - { - result += Reader.Value; - if (!Reader.Read()) - { - break; - } - } - return result; - } // 0x6018 private static uint s_isTextualNodeBitmap = (1 << (int)XmlNodeType.Text) | (1 << (int)XmlNodeType.CDATA) | (1 << (int)XmlNodeType.Whitespace) | (1 << (int)XmlNodeType.SignificantWhitespace); diff --git a/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs b/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs index 25fdb4df57..d9f998ba0f 100644 --- a/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs +++ b/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs @@ -807,7 +807,7 @@ namespace System.Xml.Serialization #if !FEATURE_SERIALIZATION_UAPAOT [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] [ResourceExposure(ResourceScope.None)] - public static bool GenerateSerializer(Type[] types, XmlMapping[] mappings, Stream stream) + internal static bool GenerateSerializer(Type[] types, XmlMapping[] mappings, Stream stream) { if (types == null || types.Length == 0) return false; diff --git a/src/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryContext.cs b/src/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryContext.cs index 2a747d6e2a..783692d486 100644 --- a/src/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryContext.cs +++ b/src/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryContext.cs @@ -328,9 +328,7 @@ namespace System.Xml.Xsl.Runtime XsltMessageEncounteredEventHandler onMessage = (_argList != null) ? _argList.xsltMessageEncountered : null; if (onMessage != null) - onMessage(this, new XmlILQueryEventArgs(message)); - else - Console.WriteLine(message); + onMessage(this, new XmlILQueryEventArgs(message)); } } diff --git a/src/System.Private.Xml/src/System/Xml/Xsl/XsltOld/MessageAction.cs b/src/System.Private.Xml/src/System/Xml/Xsl/XsltOld/MessageAction.cs index 4910cb122e..6c57672caf 100644 --- a/src/System.Private.Xml/src/System/Xml/Xsl/XsltOld/MessageAction.cs +++ b/src/System.Private.Xml/src/System/Xml/Xsl/XsltOld/MessageAction.cs @@ -56,8 +56,7 @@ namespace System.Xml.Xsl.XsltOld case ProcessingChildren: TextOnlyOutput recOutput = processor.PopOutput() as TextOnlyOutput; - Debug.Assert(recOutput != null); - Console.WriteLine(recOutput.Writer.ToString()); + Debug.Assert(recOutput != null); if (_Terminate) { diff --git a/src/System.Private.Xml/tests/Writers/RwFactory/CXmlDriverVariation.cs b/src/System.Private.Xml/tests/Writers/RwFactory/CXmlDriverVariation.cs index f90abf777f..4f00d99d1f 100644 --- a/src/System.Private.Xml/tests/Writers/RwFactory/CXmlDriverVariation.cs +++ b/src/System.Private.Xml/tests/Writers/RwFactory/CXmlDriverVariation.cs @@ -58,8 +58,7 @@ namespace System.Xml.Tests res = (tagVARIATION_STATUS)HandleException(e); } catch (Exception e) - { - Console.WriteLine(e); + { res = (tagVARIATION_STATUS)HandleException(e); } diff --git a/src/System.Private.Xml/tests/XmlSchema/XmlSchemaSet/TC_SchemaSet_Compile.cs b/src/System.Private.Xml/tests/XmlSchema/XmlSchemaSet/TC_SchemaSet_Compile.cs index b360f5bd19..43fde8b0fa 100644 --- a/src/System.Private.Xml/tests/XmlSchema/XmlSchemaSet/TC_SchemaSet_Compile.cs +++ b/src/System.Private.Xml/tests/XmlSchema/XmlSchemaSet/TC_SchemaSet_Compile.cs @@ -85,6 +85,7 @@ namespace System.Xml.Tests Assert.True(false); } + [ActiveIssue(27740, TestPlatforms.AnyUnix)] [Fact] //[Variation(Desc = "TFS_470021 Unexpected local particle qualified name when chameleon schema is added to set")] public void TFS_470021() diff --git a/src/System.Private.Xml/tests/XmlSchema/XmlSchemaValidatorApi/ValidateMisc.cs b/src/System.Private.Xml/tests/XmlSchema/XmlSchemaValidatorApi/ValidateMisc.cs index 31e44a8e33..707709f43c 100644 --- a/src/System.Private.Xml/tests/XmlSchema/XmlSchemaValidatorApi/ValidateMisc.cs +++ b/src/System.Private.Xml/tests/XmlSchema/XmlSchemaValidatorApi/ValidateMisc.cs @@ -893,6 +893,7 @@ namespace System.Xml.Tests } //TFS_538324 + [ActiveIssue(27740, TestPlatforms.AnyUnix)] [Fact] public void XSDValidationGeneratesInvalidError_1() { @@ -919,6 +920,7 @@ namespace System.Xml.Tests } //TFS_538324 + [ActiveIssue(27740, TestPlatforms.AnyUnix)] [Fact] public void XSDValidationGeneratesInvalidError_2() { diff --git a/src/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompiledTransform.cs b/src/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompiledTransform.cs index 0c6dfaf71d..2f2649b529 100644 --- a/src/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompiledTransform.cs +++ b/src/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompiledTransform.cs @@ -609,18 +609,13 @@ namespace System.Xml.Tests AppContext.SetSwitch("Switch.System.Xml.AllowDefaultResolver", true); string expected = @"<?xml version=""1.0"" encoding=""utf-8""?><result>123</result>"; + string fileName = GetType().Name + "_" + Path.GetRandomFileName(); + string testFile = Path.Combine(Path.GetTempPath(), fileName); + string xmlFile = FullFilePath(fileName); // copy file on the local machine try { - string tempPath = Path.GetTempPath(); - string testFile = Path.Combine(tempPath, "xmlResolver_document_function.xml"); - if (File.Exists(testFile)) - { - File.SetAttributes(testFile, FileAttributes.Normal); - File.Delete(testFile); - } - string xmlFile = FullFilePath("xmlResolver_document_function.xml"); File.Copy(xmlFile, testFile, true); } catch (Exception e) @@ -629,6 +624,14 @@ namespace System.Xml.Tests _output.WriteLine("Could not copy file to local. Some other issues prevented this test from running"); return; //TEST_SKIPPED; } + finally + { + if (File.Exists(testFile)) + { + File.SetAttributes(testFile, FileAttributes.Normal); + File.Delete(testFile); + } + } // copy file on the local machine (this is now done with createAPItestfiles.js, see Oasys scenario.) if (LoadXSL("xmlResolver_document_function_absolute_uri.xsl", xslInputType, readerType) == 1) @@ -2522,18 +2525,13 @@ namespace System.Xml.Tests AppContext.SetSwitch("Switch.System.Xml.AllowDefaultResolver", true); string expected = @"<?xml version=""1.0"" encoding=""utf-8""?><result>123</result>"; + string fileName = GetType().Name + "_" + Path.GetRandomFileName(); + string testFile = Path.Combine(Path.GetTempPath(), fileName); + string xmlFile = FullFilePath(fileName); // copy file on the local machine try { - string tempPath = Path.GetTempPath(); - string testFile = Path.Combine(tempPath, "xmlResolver_document_function.xml"); - if (File.Exists(testFile)) - { - File.SetAttributes(testFile, FileAttributes.Normal); - File.Delete(testFile); - } - string xmlFile = FullFilePath("xmlResolver_document_function.xml"); File.Copy(xmlFile, testFile, true); } catch (Exception e) @@ -2542,6 +2540,14 @@ namespace System.Xml.Tests _output.WriteLine("Could not copy file to local. Some other issues prevented this test from running"); return; //TEST_SKIPPED; } + finally + { + if (File.Exists(testFile)) + { + File.SetAttributes(testFile, FileAttributes.Normal); + File.Delete(testFile); + } + } if (LoadXSL("xmlResolver_document_function_absolute_uri.xsl", xslInputType, readerType) == 1) { diff --git a/src/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XsltApiV2.cs b/src/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XsltApiV2.cs index e06d0a74e2..f5c7c15859 100644 --- a/src/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XsltApiV2.cs +++ b/src/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XsltApiV2.cs @@ -76,7 +76,7 @@ namespace System.Xml.Tests static XsltApiTestCaseBase2() { // Replace absolute URI in xmlResolver_document_function.xml based on the environment - string targetFile = Path.Combine(Path.GetTempPath(), "xmlResolver_document_function.xml"); + string targetFile = Path.Combine(Path.GetTempPath(), typeof(XsltApiTestCaseBase2) + "_" + Path.GetRandomFileName()); string xslFile = Path.Combine("TestFiles", FilePathUtil.GetTestDataPath(), "XsltApiV2", "xmlResolver_document_function_absolute_uri.xsl"); XmlDocument doc = new XmlDocument(); doc.Load(xslFile); diff --git a/src/System.Private.Xml/tests/Xslt/XslTransformApi/XSLTransform.cs b/src/System.Private.Xml/tests/Xslt/XslTransformApi/XSLTransform.cs index 010efa92a2..3ef33bd4fc 100644 --- a/src/System.Private.Xml/tests/Xslt/XslTransformApi/XSLTransform.cs +++ b/src/System.Private.Xml/tests/Xslt/XslTransformApi/XSLTransform.cs @@ -49,7 +49,7 @@ namespace System.Xml.Tests public class XsltApiTestCaseBase : FileCleanupTestBase { private const string XmlResolverDocumentName = "xmlResolver_document_function.xml"; - private static readonly string s_temporaryResolverDocumentFullName = Path.Combine(Path.GetTempPath(), "XslTransformApi", XmlResolverDocumentName); + private static readonly string s_temporaryResolverDocumentFullName = Path.Combine(Path.GetTempPath(), typeof(XsltApiTestCaseBase) + "_" + Path.GetRandomFileName()); private static readonly object s_temporaryResolverDocumentLock = new object(); // Generic data for all derived test cases diff --git a/src/System.Runtime.Caching/src/MatchingRefApiCompatBaseline.txt b/src/System.Runtime.Caching/src/MatchingRefApiCompatBaseline.txt deleted file mode 100644 index ad5de574e0..0000000000 --- a/src/System.Runtime.Caching/src/MatchingRefApiCompatBaseline.txt +++ /dev/null @@ -1,6 +0,0 @@ -Compat issues with assembly System.Runtime.Caching: -TypesMustExist : Type 'System.Runtime.Caching.Configuration.CachingSectionGroup' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'System.Runtime.Caching.Configuration.MemoryCacheElement' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'System.Runtime.Caching.Configuration.MemoryCacheSection' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'System.Runtime.Caching.Configuration.MemoryCacheSettingsCollection' does not exist in the implementation but it does exist in the contract. -Total Issues: 4 diff --git a/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/CachingSectionGroup.cs b/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/CachingSectionGroup.cs index e9a6219214..c18f96a83d 100644 --- a/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/CachingSectionGroup.cs +++ b/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/CachingSectionGroup.cs @@ -7,7 +7,7 @@ using System.Configuration; namespace System.Runtime.Caching.Configuration { - public sealed class CachingSectionGroup : ConfigurationSectionGroup + internal sealed class CachingSectionGroup : ConfigurationSectionGroup { public CachingSectionGroup() { diff --git a/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/MemoryCacheElement.cs b/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/MemoryCacheElement.cs index cba10e3936..c0e1997c24 100644 --- a/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/MemoryCacheElement.cs +++ b/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/MemoryCacheElement.cs @@ -8,7 +8,7 @@ using System.Configuration; namespace System.Runtime.Caching.Configuration { - public sealed class MemoryCacheElement : ConfigurationElement + internal sealed class MemoryCacheElement : ConfigurationElement { private static ConfigurationPropertyCollection s_properties; private static readonly ConfigurationProperty s_propName; diff --git a/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/MemoryCacheSection.cs b/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/MemoryCacheSection.cs index 06afa86dd9..20b8cc60cb 100644 --- a/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/MemoryCacheSection.cs +++ b/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/MemoryCacheSection.cs @@ -21,7 +21,7 @@ namespace System.Runtime.Caching.Configuration </system.caching> */ - public sealed class MemoryCacheSection : ConfigurationSection + internal sealed class MemoryCacheSection : ConfigurationSection { private static ConfigurationPropertyCollection s_properties; private static readonly ConfigurationProperty s_propNamedCaches; diff --git a/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/MemoryCacheSettingsCollection.cs b/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/MemoryCacheSettingsCollection.cs index f35e5625f4..ba976621c6 100644 --- a/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/MemoryCacheSettingsCollection.cs +++ b/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/MemoryCacheSettingsCollection.cs @@ -9,7 +9,7 @@ namespace System.Runtime.Caching.Configuration { [ConfigurationCollection(typeof(MemoryCacheElement), CollectionType = ConfigurationElementCollectionType.AddRemoveClearMap)] - public sealed class MemoryCacheSettingsCollection : ConfigurationElementCollection + internal sealed class MemoryCacheSettingsCollection : ConfigurationElementCollection { private static ConfigurationPropertyCollection s_properties; diff --git a/src/System.Runtime.Serialization.Xml/src/MatchingRefApiCompatBaseline.txt b/src/System.Runtime.Serialization.Xml/src/MatchingRefApiCompatBaseline.txt deleted file mode 100644 index cd47ce98d8..0000000000 --- a/src/System.Runtime.Serialization.Xml/src/MatchingRefApiCompatBaseline.txt +++ /dev/null @@ -1,3 +0,0 @@ -Compat issues with assembly System.Runtime.Serialization.Xml: -MembersMustExist : Member 'System.Runtime.Serialization.DataContractSerializer..ctor(System.Type, System.Xml.XmlDictionaryString, System.Xml.XmlDictionaryString, System.Collections.Generic.IEnumerable<System.Type>, System.Int32, System.Boolean, System.Boolean, System.Runtime.Serialization.DataContractResolver)' does not exist in the implementation but it does exist in the contract. -Total Issues: 1 diff --git a/src/System.Runtime/ref/System.Runtime.cs b/src/System.Runtime/ref/System.Runtime.cs index 66fee8c70e..a7801dd2be 100644 --- a/src/System.Runtime/ref/System.Runtime.cs +++ b/src/System.Runtime/ref/System.Runtime.cs @@ -1685,6 +1685,7 @@ namespace System public readonly partial struct Memory<T> { private readonly object _dummy; + public Memory(System.Buffers.MemoryManager<T> manager, int start, int length) { throw null; } public Memory(T[] array) { throw null; } public Memory(T[] array, int start, int length) { throw null; } public static System.Memory<T> Empty { get { throw null; } } @@ -1692,6 +1693,8 @@ namespace System public int Length { get { throw null; } } public System.Span<T> Span { get { throw null; } } public void CopyTo(System.Memory<T> destination) { } + [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] + public static System.Memory<T> CreateFromPinnedArray(T[] array, int start, int length) { throw null; } public bool Equals(System.Memory<T> other) { throw null; } [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] public override bool Equals(object obj) { throw null; } @@ -1701,7 +1704,6 @@ namespace System public static implicit operator System.ReadOnlyMemory<T> (System.Memory<T> memory) { throw null; } public static implicit operator System.Memory<T> (T[] array) { throw null; } public System.Buffers.MemoryHandle Pin() { throw null; } - public System.Buffers.MemoryHandle Retain(bool pin = false) { throw null; } public System.Memory<T> Slice(int start) { throw null; } public System.Memory<T> Slice(int start, int length) { throw null; } public T[] ToArray() { throw null; } @@ -1949,7 +1951,6 @@ namespace System public static implicit operator System.ReadOnlyMemory<T> (System.ArraySegment<T> segment) { throw null; } public static implicit operator System.ReadOnlyMemory<T> (T[] array) { throw null; } public System.Buffers.MemoryHandle Pin() { throw null; } - public System.Buffers.MemoryHandle Retain(bool pin = false) { throw null; } public System.ReadOnlyMemory<T> Slice(int start) { throw null; } public System.ReadOnlyMemory<T> Slice(int start, int length) { throw null; } public T[] ToArray() { throw null; } @@ -3704,35 +3705,35 @@ namespace System } namespace System.Buffers { - public partial interface IRetainable + public partial interface IMemoryOwner<T> : System.IDisposable + { + System.Memory<T> Memory { get; } + } + public partial interface IPinnable { - bool Release(); - void Retain(); + System.Buffers.MemoryHandle Pin(int elementIndex); + void Unpin(); } public partial struct MemoryHandle : System.IDisposable { private object _dummy; [System.CLSCompliantAttribute(false)] - public unsafe MemoryHandle(System.Buffers.IRetainable retainable, void* pointer = null, System.Runtime.InteropServices.GCHandle handle = default(System.Runtime.InteropServices.GCHandle)) { throw null; } - public bool HasPointer { get { throw null; } } + public unsafe MemoryHandle(void* pointer, System.Runtime.InteropServices.GCHandle handle = default(System.Runtime.InteropServices.GCHandle), System.Buffers.IPinnable pinnable = null) { throw null; } [System.CLSCompliantAttribute(false)] public unsafe void* Pointer { get { throw null; } } public void Dispose() { } } - public abstract partial class OwnedMemory<T> : System.Buffers.IRetainable, System.IDisposable + public abstract partial class MemoryManager<T> : System.Buffers.IMemoryOwner<T>, System.Buffers.IPinnable, System.IDisposable { - protected OwnedMemory() { } - public abstract bool IsDisposed { get; } - protected abstract bool IsRetained { get; } - public abstract int Length { get; } - public System.Memory<T> Memory { get { throw null; } } - public abstract System.Span<T> Span { get; } - public void Dispose() { } + protected MemoryManager() { } + public virtual int Length { get { throw null; } } + public virtual System.Memory<T> Memory { get { throw null; } } protected abstract void Dispose(bool disposing); - public abstract System.Buffers.MemoryHandle Pin(int byteOffset = 0); - public abstract bool Release(); - public abstract void Retain(); - protected internal abstract bool TryGetArray(out System.ArraySegment<T> segment); + public abstract System.Span<T> GetSpan(); + public abstract System.Buffers.MemoryHandle Pin(int elementIndex = 0); + void System.IDisposable.Dispose() { } + protected internal virtual bool TryGetArray(out System.ArraySegment<T> segment) { throw null; } + public abstract void Unpin(); } public delegate void ReadOnlySpanAction<T, in TArg>(System.ReadOnlySpan<T> span, TArg arg); public delegate void SpanAction<T, in TArg>(System.Span<T> span, TArg arg); @@ -6636,10 +6637,10 @@ namespace System.Runtime.CompilerServices } public static partial class RuntimeFeature { - public const string PortablePdb = "PortablePdb"; #if FEATURE_DEFAULT_INTERFACES public const string DefaultImplementationsOfInterfaces = "DefaultImplementationsOfInterfaces"; #endif + public const string PortablePdb = "PortablePdb"; public static bool IsSupported(string feature) { throw null; } } public static partial class RuntimeHelpers @@ -8002,9 +8003,9 @@ namespace System.Threading.Tasks [System.Runtime.CompilerServices.AsyncMethodBuilderAttribute(typeof(System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder))] public readonly partial struct ValueTask : System.IEquatable<System.Threading.Tasks.ValueTask> { - internal readonly object _dummy; - public ValueTask(System.Threading.Tasks.Task task) { throw null; } + private readonly object _dummy; public ValueTask(System.Threading.Tasks.Sources.IValueTaskSource source, short token) { throw null; } + public ValueTask(System.Threading.Tasks.Task task) { throw null; } public bool IsCanceled { get { throw null; } } public bool IsCompleted { get { throw null; } } public bool IsCompletedSuccessfully { get { throw null; } } @@ -8015,16 +8016,16 @@ namespace System.Threading.Tasks public bool Equals(System.Threading.Tasks.ValueTask other) { throw null; } public System.Runtime.CompilerServices.ValueTaskAwaiter GetAwaiter() { throw null; } public override int GetHashCode() { throw null; } - public System.Threading.Tasks.ValueTask Preserve() { throw null; } public static bool operator ==(System.Threading.Tasks.ValueTask left, System.Threading.Tasks.ValueTask right) { throw null; } public static bool operator !=(System.Threading.Tasks.ValueTask left, System.Threading.Tasks.ValueTask right) { throw null; } + public System.Threading.Tasks.ValueTask Preserve() { throw null; } } [System.Runtime.CompilerServices.AsyncMethodBuilderAttribute(typeof(System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder<>))] public readonly partial struct ValueTask<TResult> : System.IEquatable<System.Threading.Tasks.ValueTask<TResult>> { internal readonly TResult _result; - public ValueTask(System.Threading.Tasks.Task<TResult> task) { throw null; } public ValueTask(System.Threading.Tasks.Sources.IValueTaskSource<TResult> source, short token) { throw null; } + public ValueTask(System.Threading.Tasks.Task<TResult> task) { throw null; } public ValueTask(TResult result) { throw null; } public bool IsCanceled { get { throw null; } } public bool IsCompleted { get { throw null; } } @@ -8037,38 +8038,38 @@ namespace System.Threading.Tasks public bool Equals(System.Threading.Tasks.ValueTask<TResult> other) { throw null; } public System.Runtime.CompilerServices.ValueTaskAwaiter<TResult> GetAwaiter() { throw null; } public override int GetHashCode() { throw null; } - public System.Threading.Tasks.ValueTask<TResult> Preserve() { throw null; } public static bool operator ==(System.Threading.Tasks.ValueTask<TResult> left, System.Threading.Tasks.ValueTask<TResult> right) { throw null; } public static bool operator !=(System.Threading.Tasks.ValueTask<TResult> left, System.Threading.Tasks.ValueTask<TResult> right) { throw null; } + public System.Threading.Tasks.ValueTask<TResult> Preserve() { throw null; } public override string ToString() { throw null; } } } namespace System.Threading.Tasks.Sources { - [Flags] - public enum ValueTaskSourceOnCompletedFlags - { - None, - UseSchedulingContext = 0x1, - FlowExecutionContext = 0x2, - } - public enum ValueTaskSourceStatus - { - Pending = 0, - Succeeded = 1, - Faulted = 2, - Canceled = 3 - } - public interface IValueTaskSource + public partial interface IValueTaskSource { + void GetResult(short token); System.Threading.Tasks.Sources.ValueTaskSourceStatus GetStatus(short token); void OnCompleted(System.Action<object> continuation, object state, short token, System.Threading.Tasks.Sources.ValueTaskSourceOnCompletedFlags flags); - void GetResult(short token); } - public interface IValueTaskSource<out TResult> + public partial interface IValueTaskSource<out TResult> { + TResult GetResult(short token); System.Threading.Tasks.Sources.ValueTaskSourceStatus GetStatus(short token); void OnCompleted(System.Action<object> continuation, object state, short token, System.Threading.Tasks.Sources.ValueTaskSourceOnCompletedFlags flags); - TResult GetResult(short token); + } + [System.FlagsAttribute] + public enum ValueTaskSourceOnCompletedFlags + { + FlowExecutionContext = 2, + None = 0, + UseSchedulingContext = 1, + } + public enum ValueTaskSourceStatus + { + Canceled = 3, + Faulted = 2, + Pending = 0, + Succeeded = 1, } } diff --git a/src/System.Runtime/src/ApiCompatBaseline.uap.txt b/src/System.Runtime/src/ApiCompatBaseline.uap.txt new file mode 100644 index 0000000000..4420be7a23 --- /dev/null +++ b/src/System.Runtime/src/ApiCompatBaseline.uap.txt @@ -0,0 +1,10 @@ +Compat issues with assembly System.Runtime: +MembersMustExist : Member 'System.Memory<T>.Pin()' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.ReadOnlyMemory<T>.Pin()' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Memory<T>..ctor(System.Buffers.MemoryManager<T>, System.Int32, System.Int32)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Memory<T>.CreateFromPinnedArray(T[], System.Int32, System.Int32)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Buffers.MemoryHandle..ctor(System.Void*, System.Runtime.InteropServices.GCHandle, System.Buffers.IPinnable)' does not exist in the implementation but it does exist in the contract. +TypesMustExist : Type 'System.Buffers.IMemoryOwner<T>' does not exist in the implementation but it does exist in the contract. +TypesMustExist : Type 'System.Buffers.IPinnable' does not exist in the implementation but it does exist in the contract. +TypesMustExist : Type 'System.Buffers.MemoryManager<T>' does not exist in the implementation but it does exist in the contract. +Total Issues: 8 diff --git a/src/System.Runtime/src/ApiCompatBaseline.uapaot.txt b/src/System.Runtime/src/ApiCompatBaseline.uapaot.txt index ba98be8651..4420be7a23 100644 --- a/src/System.Runtime/src/ApiCompatBaseline.uapaot.txt +++ b/src/System.Runtime/src/ApiCompatBaseline.uapaot.txt @@ -1,4 +1,10 @@ Compat issues with assembly System.Runtime: MembersMustExist : Member 'System.Memory<T>.Pin()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'System.ReadOnlyMemory<T>.Pin()' does not exist in the implementation but it does exist in the contract. -Total Issues: 2 +MembersMustExist : Member 'System.Memory<T>..ctor(System.Buffers.MemoryManager<T>, System.Int32, System.Int32)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Memory<T>.CreateFromPinnedArray(T[], System.Int32, System.Int32)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Buffers.MemoryHandle..ctor(System.Void*, System.Runtime.InteropServices.GCHandle, System.Buffers.IPinnable)' does not exist in the implementation but it does exist in the contract. +TypesMustExist : Type 'System.Buffers.IMemoryOwner<T>' does not exist in the implementation but it does exist in the contract. +TypesMustExist : Type 'System.Buffers.IPinnable' does not exist in the implementation but it does exist in the contract. +TypesMustExist : Type 'System.Buffers.MemoryManager<T>' does not exist in the implementation but it does exist in the contract. +Total Issues: 8 diff --git a/src/System.Runtime/src/System.Runtime.csproj b/src/System.Runtime/src/System.Runtime.csproj index 1a80306018..428bbf0137 100644 --- a/src/System.Runtime/src/System.Runtime.csproj +++ b/src/System.Runtime/src/System.Runtime.csproj @@ -6,6 +6,7 @@ <AssemblyName>System.Runtime</AssemblyName> <IsPartialFacadeAssembly>true</IsPartialFacadeAssembly> <ILLinkClearInitLocals>true</ILLinkClearInitLocals> + <GenFacadesIgnoreMissingTypes Condition="'$(TargetGroup)'=='uapaot' OR '$(TargetGroup)' == 'uap'">true</GenFacadesIgnoreMissingTypes> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Unix-Debug|AnyCPU'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Unix-Release|AnyCPU'" /> diff --git a/src/System.Security.Cryptography.Csp/src/Internal/Cryptography/Helpers.cs b/src/System.Security.Cryptography.Csp/src/Internal/Cryptography/Helpers.cs index 6ba101eddd..9e0d2b42b4 100644 --- a/src/System.Security.Cryptography.Csp/src/Internal/Cryptography/Helpers.cs +++ b/src/System.Security.Cryptography.Csp/src/Internal/Cryptography/Helpers.cs @@ -44,6 +44,20 @@ namespace Internal.Cryptography return null; } + public static byte[] TrimLargeIV(byte[] currentIV, int blockSizeInBits) + { + int blockSizeBytes = checked((blockSizeInBits + 7) / 8); + + if (currentIV?.Length > blockSizeBytes) + { + byte[] tmp = new byte[blockSizeBytes]; + Buffer.BlockCopy(currentIV, 0, tmp, 0, tmp.Length); + return tmp; + } + + return currentIV; + } + public static bool IsLegalSize(this int size, KeySizes[] legalSizes) { for (int i = 0; i < legalSizes.Length; i++) diff --git a/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/DESCryptoServiceProvider.Unix.cs b/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/DESCryptoServiceProvider.Unix.cs index 790651581f..46a345b88c 100644 --- a/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/DESCryptoServiceProvider.Unix.cs +++ b/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/DESCryptoServiceProvider.Unix.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.ComponentModel; +using Internal.Cryptography; namespace System.Security.Cryptography { @@ -26,9 +27,13 @@ namespace System.Security.Cryptography } public override ICryptoTransform CreateDecryptor() => _impl.CreateDecryptor(); - public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] rgbIV) => _impl.CreateDecryptor(rgbKey, rgbIV); public override ICryptoTransform CreateEncryptor() => _impl.CreateEncryptor(); - public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] rgbIV) => _impl.CreateEncryptor(rgbKey, rgbIV); + + public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] rgbIV) => + _impl.CreateEncryptor(rgbKey, Helpers.TrimLargeIV(rgbIV, BlockSize)); + + public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] rgbIV) => + _impl.CreateDecryptor(rgbKey, Helpers.TrimLargeIV(rgbIV, BlockSize)); protected override void Dispose(bool disposing) { diff --git a/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/RC2CryptoServiceProvider.Unix.cs b/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/RC2CryptoServiceProvider.Unix.cs index a4360a81dc..4b9deafe56 100644 --- a/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/RC2CryptoServiceProvider.Unix.cs +++ b/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/RC2CryptoServiceProvider.Unix.cs @@ -29,9 +29,13 @@ namespace System.Security.Cryptography } public override ICryptoTransform CreateDecryptor() => _impl.CreateDecryptor(); - public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] rgbIV) => _impl.CreateDecryptor(rgbKey, rgbIV); public override ICryptoTransform CreateEncryptor() => _impl.CreateEncryptor(); - public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] rgbIV) => _impl.CreateEncryptor(rgbKey, rgbIV); + + public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] rgbIV) => + _impl.CreateEncryptor(rgbKey, Helpers.TrimLargeIV(rgbIV, BlockSize)); + + public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] rgbIV) => + _impl.CreateDecryptor(rgbKey, Helpers.TrimLargeIV(rgbIV, BlockSize)); protected override void Dispose(bool disposing) { diff --git a/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/TripleDESCryptoServiceProvider.cs b/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/TripleDESCryptoServiceProvider.cs index 0026a960a6..07b1864810 100644 --- a/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/TripleDESCryptoServiceProvider.cs +++ b/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/TripleDESCryptoServiceProvider.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.ComponentModel; +using Internal.Cryptography; namespace System.Security.Cryptography { @@ -63,12 +64,16 @@ namespace System.Security.Cryptography public override KeySizes[] LegalBlockSizes => _impl.LegalBlockSizes; public override KeySizes[] LegalKeySizes => _impl.LegalKeySizes; public override ICryptoTransform CreateEncryptor() => _impl.CreateEncryptor(); - public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] rgbIV) => _impl.CreateEncryptor(rgbKey, rgbIV); public override ICryptoTransform CreateDecryptor() => _impl.CreateDecryptor(); - public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] rgbIV) => _impl.CreateDecryptor(rgbKey, rgbIV); public override void GenerateIV() => _impl.GenerateIV(); public override void GenerateKey() => _impl.GenerateKey(); + public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] rgbIV) => + _impl.CreateEncryptor(rgbKey, Helpers.TrimLargeIV(rgbIV, BlockSize)); + + public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] rgbIV) => + _impl.CreateDecryptor(rgbKey, Helpers.TrimLargeIV(rgbIV, BlockSize)); + protected override void Dispose(bool disposing) { if (disposing) diff --git a/src/System.Security.Cryptography.Csp/tests/CreateTransformCompat.cs b/src/System.Security.Cryptography.Csp/tests/CreateTransformCompat.cs new file mode 100644 index 0000000000..41d14ec3f4 --- /dev/null +++ b/src/System.Security.Cryptography.Csp/tests/CreateTransformCompat.cs @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Security.Cryptography.Csp.Tests +{ + public static class CreateTransformCompat + { + [Theory] + [InlineData(typeof(AesCryptoServiceProvider), null)] + [InlineData(typeof(DESCryptoServiceProvider), 9)] + [InlineData(typeof(DESCryptoServiceProvider), 13)] + [InlineData(typeof(RC2CryptoServiceProvider), 9)] + [InlineData(typeof(RC2CryptoServiceProvider), 17)] + [InlineData(typeof(TripleDESCryptoServiceProvider), 9)] + [InlineData(typeof(TripleDESCryptoServiceProvider), 24)] + [InlineData(typeof(TripleDESCryptoServiceProvider), 31)] + public static void CreateTransform_IVTooBig(Type t, int? ivSizeBytes) + { + using (SymmetricAlgorithm alg = (SymmetricAlgorithm)Activator.CreateInstance(t)) + { + alg.Mode = CipherMode.CBC; + byte[] key = alg.Key; + + // If it isn't supposed to work + if (ivSizeBytes == null) + { + // badSize is in bytes, BlockSize is in bits. + // So badSize is 8 times as big as it should be. + int badSize = alg.BlockSize; + Assert.Throws<ArgumentException>(() => alg.CreateEncryptor(key, new byte[badSize])); + Assert.Throws<ArgumentException>(() => alg.CreateDecryptor(key, new byte[badSize])); + + return; + } + + int correctSize = alg.BlockSize / 8; + byte[] data = { 1, 2, 3, 4, 5 }; + + byte[] iv = new byte[ivSizeBytes.Value]; + + for (int i = 0; i < iv.Length; i++) + { + iv[i] = (byte)((byte.MaxValue - i) ^ correctSize); + } + + byte[] correctIV = iv.AsSpan(0, correctSize).ToArray(); + + using (ICryptoTransform correctEnc = alg.CreateEncryptor(key, correctIV)) + using (ICryptoTransform badIvEnc = alg.CreateEncryptor(key, iv)) + using (ICryptoTransform badIvDec = alg.CreateDecryptor(key, iv)) + { + byte[] encrypted = badIvEnc.TransformFinalBlock(data, 0, data.Length); + byte[] correctEncrypted = correctEnc.TransformFinalBlock(data, 0, data.Length); + + Assert.Equal(correctEncrypted, encrypted); + + byte[] decrypted1 = badIvDec.TransformFinalBlock(correctEncrypted, 0, correctEncrypted.Length); + + Assert.Equal(data, decrypted1); + } + } + } + } +} diff --git a/src/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj b/src/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj index 4b0983cf44..ea0a3332a6 100644 --- a/src/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj +++ b/src/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj @@ -13,6 +13,7 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Windows_NT-Debug|AnyCPU'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Windows_NT-Release|AnyCPU'" /> <ItemGroup> + <Compile Include="CreateTransformCompat.cs" /> <Compile Include="CspParametersTests.cs" /> <Compile Include="RSAImportExportCspBlobTests.cs" /> <Compile Include="RSACryptoServiceProviderBackCompat.cs" /> diff --git a/src/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs b/src/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs index 95492b5ba3..7d34e05a18 100644 --- a/src/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs +++ b/src/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs @@ -642,7 +642,12 @@ namespace System.Security.Cryptography.X509Certificates.Tests } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { - expectedFlags = X509ChainStatusFlags.UntrustedRoot; + // For OSX alone expectedFlags here means OR instead of AND. + // Because the error code changed in 10.13.4 from UntrustedRoot to PartialChain + // and we handle that later in this test. + expectedFlags = + X509ChainStatusFlags.UntrustedRoot | + X509ChainStatusFlags.PartialChain; } else { @@ -670,6 +675,18 @@ namespace System.Security.Cryptography.X509Certificates.Tests X509ChainStatusFlags.NoError, (a, b) => a | b); + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + // If we're on 10.13.3 or older we get UntrustedRoot. + // If we're on 10.13.4 or newer we get PartialChain. + // + // So make the expectedValue be whichever of those two is set. + expectedFlags = (expectedFlags & allFlags); + // One of them has to be set. + Assert.NotEqual(X509ChainStatusFlags.NoError, expectedFlags); + // Continue executing now to ensure that no other unexpected flags were set. + } + Assert.Equal(expectedFlags, allFlags); } } diff --git a/src/System.Threading.Thread/tests/ThreadTests.cs b/src/System.Threading.Thread/tests/ThreadTests.cs index 091ed868d4..78ab0bbe3d 100644 --- a/src/System.Threading.Thread/tests/ThreadTests.cs +++ b/src/System.Threading.Thread/tests/ThreadTests.cs @@ -26,6 +26,11 @@ namespace System.Threading.Threads.Tests [Fact] public static void ConstructorTest() { + const int SmallStackSize = 64 << 10; // 64 KB, currently accepted in all supported platforms, and is the PAL minimum + const int LargeStackSize = 2 << 20; // 2 MB, see https://github.com/dotnet/coreclr/issues/17170 + + int pageSizeBytes = Environment.SystemPageSize; + Action<Thread> startThreadAndJoin = t => { @@ -38,22 +43,25 @@ namespace System.Threading.Threads.Tests { // Try to stack-allocate an array to verify that close to the expected amount of stack space is actually // available - int bufferSizeBytes = Math.Max(16 << 10, stackSizeBytes - (64 << 10)); + int bufferSizeBytes = Math.Max(16 << 10, stackSizeBytes - SmallStackSize); unsafe { byte* buffer = stackalloc byte[bufferSizeBytes]; - Volatile.Write(ref buffer[0], 0xff); + for (int i = 0; i < bufferSizeBytes; i += pageSizeBytes) + { + Volatile.Write(ref buffer[i], 0xff); + } Volatile.Write(ref buffer[bufferSizeBytes - 1], 0xff); } }; startThreadAndJoin(new Thread(() => verifyStackSize(0))); startThreadAndJoin(new Thread(() => verifyStackSize(0), 0)); - startThreadAndJoin(new Thread(() => verifyStackSize(64 << 10), 64 << 10)); // 64 KB - startThreadAndJoin(new Thread(() => verifyStackSize(16 << 20), 16 << 20)); // 16 MB + startThreadAndJoin(new Thread(() => verifyStackSize(SmallStackSize), SmallStackSize)); + startThreadAndJoin(new Thread(() => verifyStackSize(LargeStackSize), LargeStackSize)); startThreadAndJoin(new Thread(state => verifyStackSize(0))); startThreadAndJoin(new Thread(state => verifyStackSize(0), 0)); - startThreadAndJoin(new Thread(state => verifyStackSize(64 << 10), 64 << 10)); // 64 KB - startThreadAndJoin(new Thread(state => verifyStackSize(16 << 20), 16 << 20)); // 16 MB + startThreadAndJoin(new Thread(state => verifyStackSize(SmallStackSize), SmallStackSize)); + startThreadAndJoin(new Thread(state => verifyStackSize(LargeStackSize), LargeStackSize)); Assert.Throws<ArgumentNullException>(() => new Thread((ThreadStart)null)); Assert.Throws<ArgumentNullException>(() => new Thread((ThreadStart)null, 0)); diff --git a/src/System.Xml.XmlSerializer/ref/System.Xml.XmlSerializer.cs b/src/System.Xml.XmlSerializer/ref/System.Xml.XmlSerializer.cs index 104b78a53d..ae074fa0ef 100644 --- a/src/System.Xml.XmlSerializer/ref/System.Xml.XmlSerializer.cs +++ b/src/System.Xml.XmlSerializer/ref/System.Xml.XmlSerializer.cs @@ -382,8 +382,8 @@ namespace System.Xml.Serialization public System.Xml.XmlQualifiedName ExportTypeMapping(System.Xml.Serialization.XmlMembersMapping xmlMembersMapping) { throw null; } public void ExportTypeMapping(System.Xml.Serialization.XmlTypeMapping xmlTypeMapping) { } } - public partial class XmlSchemaImporter -//CodeDOM : System.Xml.Serialization.SchemaImporter + public partial class XmlSchemaImporter : SchemaImporter + //CodeDOM : System.Xml.Serialization.SchemaImporter { public XmlSchemaImporter(System.Xml.Serialization.XmlSchemas schemas) { } //CODEDOM public XmlSchemaImporter(System.Xml.Serialization.XmlSchemas schemas, System.Xml.Serialization.CodeGenerationOptions options, System.CodeDom.Compiler.CodeDomProvider codeProvider, System.Xml.Serialization.ImportContext context) { } @@ -402,6 +402,10 @@ namespace System.Xml.Serialization public System.Xml.Serialization.XmlTypeMapping ImportSchemaType(System.Xml.XmlQualifiedName typeName, System.Type baseType, bool baseTypeCanBeIndirect) { throw null; } public System.Xml.Serialization.XmlTypeMapping ImportTypeMapping(System.Xml.XmlQualifiedName name) { throw null; } } + public abstract class SchemaImporter + { + internal SchemaImporter() { } + } public partial class XmlSchemas : System.Collections.CollectionBase, System.Collections.Generic.IEnumerable<System.Xml.Schema.XmlSchema>, System.Collections.IEnumerable { public XmlSchemas() { } diff --git a/src/System.Xml.XmlSerializer/src/MatchingRefApiCompatBaseline.netcoreapp.txt b/src/System.Xml.XmlSerializer/src/MatchingRefApiCompatBaseline.netcoreapp.txt deleted file mode 100644 index b362774c2c..0000000000 --- a/src/System.Xml.XmlSerializer/src/MatchingRefApiCompatBaseline.netcoreapp.txt +++ /dev/null @@ -1,7 +0,0 @@ -Compat issues with assembly System.Xml.XmlSerializer: -CannotRemoveBaseTypeOrInterface : Type 'System.Xml.Serialization.XmlSchemaImporter' does not inherit from base type 'System.Xml.Serialization.SchemaImporter' in the implementation but it does in the contract. -MembersMustExist : Member 'System.Xml.Serialization.XmlSchemaImporter..ctor(System.Xml.Serialization.XmlSchemas, System.Xml.Serialization.CodeGenerationOptions, System.Xml.Serialization.ImportContext)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Xml.Serialization.XmlSchemaImporter..ctor(System.Xml.Serialization.XmlSchemas, System.Xml.Serialization.CodeIdentifiers, System.Xml.Serialization.CodeGenerationOptions)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Xml.Serialization.XmlSerializationReader.ReadString()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Xml.Serialization.XmlSerializer.GenerateSerializer(System.Type[], System.Xml.Serialization.XmlMapping[], System.IO.Stream)' does not exist in the implementation but it does exist in the contract. -Total Issues: 5 diff --git a/src/System.Xml.XmlSerializer/src/MatchingRefApiCompatBaseline.uap.txt b/src/System.Xml.XmlSerializer/src/MatchingRefApiCompatBaseline.uap.txt deleted file mode 100644 index b362774c2c..0000000000 --- a/src/System.Xml.XmlSerializer/src/MatchingRefApiCompatBaseline.uap.txt +++ /dev/null @@ -1,7 +0,0 @@ -Compat issues with assembly System.Xml.XmlSerializer: -CannotRemoveBaseTypeOrInterface : Type 'System.Xml.Serialization.XmlSchemaImporter' does not inherit from base type 'System.Xml.Serialization.SchemaImporter' in the implementation but it does in the contract. -MembersMustExist : Member 'System.Xml.Serialization.XmlSchemaImporter..ctor(System.Xml.Serialization.XmlSchemas, System.Xml.Serialization.CodeGenerationOptions, System.Xml.Serialization.ImportContext)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Xml.Serialization.XmlSchemaImporter..ctor(System.Xml.Serialization.XmlSchemas, System.Xml.Serialization.CodeIdentifiers, System.Xml.Serialization.CodeGenerationOptions)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Xml.Serialization.XmlSerializationReader.ReadString()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Xml.Serialization.XmlSerializer.GenerateSerializer(System.Type[], System.Xml.Serialization.XmlMapping[], System.IO.Stream)' does not exist in the implementation but it does exist in the contract. -Total Issues: 5 diff --git a/src/System.Xml.XmlSerializer/src/MatchingRefApiCompatBaseline.uapaot.txt b/src/System.Xml.XmlSerializer/src/MatchingRefApiCompatBaseline.uapaot.txt index 52460f4dcd..34bf6b242e 100644 --- a/src/System.Xml.XmlSerializer/src/MatchingRefApiCompatBaseline.uapaot.txt +++ b/src/System.Xml.XmlSerializer/src/MatchingRefApiCompatBaseline.uapaot.txt @@ -1,10 +1,7 @@ Compat issues with assembly System.Xml.XmlSerializer: -CannotRemoveBaseTypeOrInterface : Type 'System.Xml.Serialization.XmlSchemaImporter' does not inherit from base type 'System.Xml.Serialization.SchemaImporter' in the implementation but it does in the contract. -MembersMustExist : Member 'System.Xml.Serialization.XmlSchemaImporter..ctor(System.Xml.Serialization.XmlSchemas, System.Xml.Serialization.CodeGenerationOptions, System.Xml.Serialization.ImportContext)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Xml.Serialization.XmlSchemaImporter..ctor(System.Xml.Serialization.XmlSchemas, System.Xml.Serialization.CodeIdentifiers, System.Xml.Serialization.CodeGenerationOptions)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Xml.Serialization.XmlSerializationReader.ReadString()' does not exist in the implementation but it does exist in the contract. +# Used by uap tooling for serializer generation MembersMustExist : Member 'System.String System.Xml.Serialization.XmlSerializer.DefaultNamespace' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'System.Xml.Serialization.XmlSerializer.Mode.get()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'System.Xml.Serialization.XmlSerializer.Mode.set(System.Xml.Serialization.SerializationMode)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'System.Xml.Serialization.XmlSerializer.SetXmlSerializerContract(System.Xml.Serialization.XmlSerializerImplementation)' does not exist in the implementation but it does exist in the contract. -Total Issues: 8 + diff --git a/src/shims/ApiCompatBaseline.uapaot.netstandard20.txt b/src/shims/ApiCompatBaseline.uapaot.netstandard20.txt index 181752efce..5064bd41f5 100644 --- a/src/shims/ApiCompatBaseline.uapaot.netstandard20.txt +++ b/src/shims/ApiCompatBaseline.uapaot.netstandard20.txt @@ -12,6 +12,14 @@ TypesMustExist : Type 'System.Security.Cryptography.ECPoint' does not exist in t Compat issues with assembly System.Runtime: MembersMustExist : Member 'System.Memory<T>.Pin()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'System.ReadOnlyMemory<T>.Pin()' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Memory<T>..ctor(System.Buffers.MemoryManager<T>, System.Int32, System.Int32)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Memory<T>.CreateFromPinnedArray(T[], System.Int32, System.Int32)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Buffers.MemoryHandle..ctor(System.Void*, System.Runtime.InteropServices.GCHandle, System.Buffers.IPinnable)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Runtime.InteropServices.MemoryMarshal.TryGetMemoryManager<T, TManager>(System.ReadOnlyMemory<T>, TManager)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Runtime.InteropServices.MemoryMarshal.TryGetMemoryManager<T, TManager>(System.ReadOnlyMemory<T>, TManager, System.Int32, System.Int32)' does not exist in the implementation but it does exist in the contract. +TypesMustExist : Type 'System.Buffers.IMemoryOwner<T>' does not exist in the implementation but it does exist in the contract. +TypesMustExist : Type 'System.Buffers.IPinnable' does not exist in the implementation but it does exist in the contract. +TypesMustExist : Type 'System.Buffers.MemoryManager<T>' does not exist in the implementation but it does exist in the contract. # Compat issues complaining about class vs delegate and class vs struct are because of a bug in APICompat tool where the implementation is picking # the wrong core assembly. It is picking System.Runtime instead of System.Private.CoreLib, there isn't any straight forward way to fix so baselining. diff --git a/tools-local/ILAsmVersion.txt b/tools-local/ILAsmVersion.txt index 207183d0af..a1d8a641d8 100644 --- a/tools-local/ILAsmVersion.txt +++ b/tools-local/ILAsmVersion.txt @@ -1 +1 @@ -2.1.0-preview3-26329-01
\ No newline at end of file +2.1.0-preview2-26403-07
\ No newline at end of file |