From ff151520812934f79245ef7552316d41ddf9f2b6 Mon Sep 17 00:00:00 2001
From: Doug Bunting <6431421+dougbu@users.noreply.github.com>
Date: Thu, 4 Aug 2022 11:51:24 -0700
Subject: [PATCH 01/19] Move NetCore project to use .NET SDK -
Formatting.NetCore now targets `netstandard1.3` - was `Profile259` aka
`portable-net45+wp80+win8+wpa81`, approximately `netstandard1.0` - but
Newtonsoft.Json.Bson supports `netstandard1.3` and up - use
Newtonsoft.Json.Bson in NetCore projects - can now multi-target NetCore.Test
as well
nits:
- align src/ Formatting projects as much as possible
- align test/ Formatting projects as much as possible
- clean up the Microsoft.TestCommon project slightly
Notes:
- NetCore project names make no sense but I'm leaving them alone for this PR
- Formatting.NetCore does not build at this point due to `netstandard1.3` / `Profile259` differences
---
RuntimePortable.sln | 2 +-
packages/repositories.config | 1 -
src/Directory.Build.props | 2 +-
.../System.Net.Http.Formatting.NetCore.csproj | 303 +++---------------
.../packages.config | 7 -
...tem.Net.Http.Formatting.NetStandard.csproj | 62 ++--
.../System.Net.Http.Formatting.csproj | 178 ++--------
.../Microsoft.TestCommon.csproj | 17 +-
...em.Net.Http.Formatting.NetCore.Test.csproj | 93 +-----
...et.Http.Formatting.NetStandard.Test.csproj | 15 +-
.../System.Net.Http.Formatting.Test.csproj | 141 +-------
11 files changed, 154 insertions(+), 667 deletions(-)
delete mode 100644 src/System.Net.Http.Formatting.NetCore/packages.config
diff --git a/RuntimePortable.sln b/RuntimePortable.sln
index bbe26d3cf..6838937f7 100644
--- a/RuntimePortable.sln
+++ b/RuntimePortable.sln
@@ -9,7 +9,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{C40883CD-3
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.TestCommon", "test\Microsoft.TestCommon\Microsoft.TestCommon.csproj", "{FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Net.Http.Formatting.NetCore", "src\System.Net.Http.Formatting.NetCore\System.Net.Http.Formatting.NetCore.csproj", "{C7060639-719B-4BD2-8A37-2F146B5A0668}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Net.Http.Formatting.NetCore", "src\System.Net.Http.Formatting.NetCore\System.Net.Http.Formatting.NetCore.csproj", "{C7060639-719B-4BD2-8A37-2F146B5A0668}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Net.Http.Formatting.NetCore.Test", "test\System.Net.Http.Formatting.NetCore.Test\System.Net.Http.Formatting.NetCore.Test.csproj", "{8DA61DAC-FF7E-4CA1-93A0-6148DB66FD08}"
EndProject
diff --git a/packages/repositories.config b/packages/repositories.config
index 22e5c94b2..18d08a3de 100644
--- a/packages/repositories.config
+++ b/packages/repositories.config
@@ -3,7 +3,6 @@
-
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index c81c39ff4..c5c258a90 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -4,6 +4,6 @@
true
false
- v4.5
+ v4.5
diff --git a/src/System.Net.Http.Formatting.NetCore/System.Net.Http.Formatting.NetCore.csproj b/src/System.Net.Http.Formatting.NetCore/System.Net.Http.Formatting.NetCore.csproj
index 6964b5560..a505b3bf0 100644
--- a/src/System.Net.Http.Formatting.NetCore/System.Net.Http.Formatting.NetCore.csproj
+++ b/src/System.Net.Http.Formatting.NetCore/System.Net.Http.Formatting.NetCore.csproj
@@ -1,279 +1,70 @@
-
-
+
- {C7060639-719B-4BD2-8A37-2F146B5A0668}
- Library
- Properties
+ netstandard1.3
System.Net.Http
System.Net.Http.Formatting
$(OutputPath)NetCore\
$(OutputPath)$(AssemblyName).xml
- $(CodeAnalysis)
- ..\Strict.ruleset
- /assemblycomparemode:StrongNameIgnoringVersion
- false
- $(DefineConstants);NETFX_CORE;ASPNETMVC;NOT_CLS_COMPLIANT
- Profile259
- {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ false
+ $(DefineConstants);ASPNETMVC;NETFX_CORE
1591
+ false
+ $(Configurations);CodeAnalysis
+ false
+
-
- Properties\CommonAssemblyInfo.cs
-
-
- Common\Error.cs
-
-
- Common\TaskHelpers.cs
-
-
- Common\TaskHelpersExtensions.cs
-
-
- Common\UriQueryUtility.cs
-
-
- Common\CollectionExtensions.cs
-
-
- Common\ListWrapperCollection.cs
-
-
- FormattingUtilities.cs
-
-
- Formatting\BaseJsonMediaTypeFormatter.cs
-
-
- Formatting\BsonMediaTypeFormatter.cs
-
-
- Formatting\DelegatingEnumerable.cs
-
-
- Formatting\FormDataCollection.cs
-
-
- Formatting\FormUrlEncodedJson.cs
-
-
- Formatting\FormUrlEncodedMediaTypeFormatter.cs
-
-
- Formatting\IFormatterLogger.cs
-
-
- Formatting\JsonMediaTypeFormatter.cs
-
-
- Formatting\MediaTypeConstants.cs
-
-
- Formatting\MediaTypeFormatter.cs
-
-
- Formatting\MediaTypeFormatterCollection.cs
-
-
- Formatting\MediaTypeHeaderValueExtensions.cs
-
-
- Formatting\MediaTypeHeaderValueRange.cs
-
-
- Formatting\ParsedMediaTypeHeaderValue.cs
-
-
- Formatting\Parsers\FormUrlEncodedParser.cs
-
-
- Formatting\Parsers\HttpRequestHeaderParser.cs
-
-
- Formatting\Parsers\HttpRequestLineParser.cs
-
-
- Formatting\Parsers\HttpResponseHeaderParser.cs
-
-
- Formatting\Parsers\HttpStatusLineParser.cs
-
-
- Formatting\Parsers\InternetMessageFormatHeaderParser.cs
-
-
- Formatting\Parsers\MimeMultipartBodyPartParser.cs
-
-
- Formatting\Parsers\MimeMultipartParser.cs
-
-
- Formatting\Parsers\ParserState.cs
-
-
- Formatting\StringComparisonHelper.cs
-
-
- Formatting\XmlMediaTypeFormatter.cs
-
-
- HttpContentFormDataExtensions.cs
-
-
- HttpValueCollection.cs
-
-
- UriExtensions.cs
-
-
-
- Handlers\HttpProgressEventArgs.cs
-
-
- Handlers\ProgressContent.cs
-
-
- Handlers\ProgressMessageHandler.cs
-
-
- Handlers\ProgressStream.cs
-
-
- HttpClientExtensions.cs
-
-
- HttpClientFactory.cs
-
-
- HttpContentExtensions.cs
-
-
- HttpContentMessageExtensions.cs
-
-
- HttpContentMultipartExtensions.cs
-
-
- HttpHeaderExtensions.cs
-
-
- HttpMessageContent.cs
-
-
- HttpUnsortedHeaders.cs
-
-
- HttpUnsortedRequest.cs
-
-
- HttpUnsortedResponse.cs
-
-
- Internal\AsyncResult.cs
-
-
- Internal\DelegatingStream.cs
-
-
- Internal\ReadOnlyStreamWithEncodingPreamble.cs
-
-
- Internal\TypeExtensions.cs
-
-
- MimeBodyPart.cs
-
-
- MultipartFileData.cs
-
-
- MultipartMemoryStreamProvider.cs
-
-
- MultipartRelatedStreamProvider.cs
-
-
- MultipartStreamProvider.cs
-
-
- ObjectContent.cs
-
-
- ObjectContentOfT.cs
-
-
- PushStreamContent.cs
-
-
- Properties\AssemblyInfo.cs
-
-
- Properties\TransparentCommonAssemblyInfo.cs
- True
- True
- Resources.resx
-
-
- UnsupportedMediaTypeException.cs
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %(RecursiveDir)\%(Filename).cs
+
+
- Properties\CommonWebApiResources.Designer.cs
True
- True
CommonWebApiResources.resx
+ True
+ Properties\CommonWebApiResources.Designer.cs
-
-
- Properties\CommonWebApiResources.resx
ResXFileCodeGenerator
CommonWebApiResources.Designer.cs
+ Properties\CommonWebApiResources.resx
-
-
+
+
+ True
+ Resources.resx
+ True
+ Properties\Resources.Designer.cs
+
- Properties\Resources.resx
ResXFileCodeGenerator
Resources.Designer.cs
+ Properties\Resources.resx
Designer
+
+
-
-
- CodeAnalysisDictionary.xml
-
-
-
-
-
-
-
- ..\..\packages\Newtonsoft.Json.13.0.1\lib\netstandard1.0\Newtonsoft.Json.dll
-
-
- ..\..\packages\Microsoft.Net.Http.2.2.13\lib\portable-net40+sl4+win8+wp71\System.Net.Http.dll
-
-
- ..\..\packages\Microsoft.Net.Http.2.2.13\lib\portable-net40+sl4+win8+wp71\System.Net.Http.Extensions.dll
-
-
- ..\..\packages\Microsoft.Net.Http.2.2.13\lib\portable-net40+sl4+win8+wp71\System.Net.Http.Primitives.dll
-
-
-
-
-
-
- This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
-
\ No newline at end of file
+
diff --git a/src/System.Net.Http.Formatting.NetCore/packages.config b/src/System.Net.Http.Formatting.NetCore/packages.config
deleted file mode 100644
index ba452cd0f..000000000
--- a/src/System.Net.Http.Formatting.NetCore/packages.config
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/System.Net.Http.Formatting.NetStandard/System.Net.Http.Formatting.NetStandard.csproj b/src/System.Net.Http.Formatting.NetStandard/System.Net.Http.Formatting.NetStandard.csproj
index 2021cff78..001e3c91e 100644
--- a/src/System.Net.Http.Formatting.NetStandard/System.Net.Http.Formatting.NetStandard.csproj
+++ b/src/System.Net.Http.Formatting.NetStandard/System.Net.Http.Formatting.NetStandard.csproj
@@ -1,4 +1,4 @@
-
+
netstandard2.0
@@ -13,67 +13,49 @@
$(Configurations);CodeAnalysis
false
+
-
-
-
- Properties\CommonAssemblyInfo.cs
-
-
- Common\CollectionExtensions.cs
-
-
- Common\Error.cs
-
-
- Common\ListWrapperCollection.cs
-
-
- Common\TaskHelpers.cs
-
-
- Common\TaskHelpersExtensions.cs
-
-
- Common\UriQueryUtility.cs
-
+
+
+
+
+
+
-
+
%(RecursiveDir)\%(Filename).cs
-
-
+
- Properties\CommonWebApiResources.Designer.cs
True
- True
CommonWebApiResources.resx
+ True
+ Properties\CommonWebApiResources.Designer.cs
- Properties\CommonWebApiResources.resx
ResXFileCodeGenerator
CommonWebApiResources.Designer.cs
+ Properties\CommonWebApiResources.resx
-
-
-
+
+
True
- True
Resources.resx
+ True
+ Properties\Resources.Designer.cs
- Properties\Resources.resx
ResXFileCodeGenerator
Resources.Designer.cs
+ Properties\Resources.resx
Designer
-
-
-
- CodeAnalysisDictionary.xml
-
+
+
diff --git a/src/System.Net.Http.Formatting/System.Net.Http.Formatting.csproj b/src/System.Net.Http.Formatting/System.Net.Http.Formatting.csproj
index 793e175a8..d871af3af 100644
--- a/src/System.Net.Http.Formatting/System.Net.Http.Formatting.csproj
+++ b/src/System.Net.Http.Formatting/System.Net.Http.Formatting.csproj
@@ -14,7 +14,17 @@
Client
1591
+
+
+
+
+
+
+
+
+
+
..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll
False
@@ -25,162 +35,44 @@
False
False
-
-
-
-
-
-
-
-
-
-
-
- Properties\CommonAssemblyInfo.cs
-
-
- Common\CollectionExtensions.cs
-
-
- Common\Error.cs
-
-
- Common\ListWrapperCollection.cs
-
-
- Common\TaskHelpers.cs
-
-
- Common\TaskHelpersExtensions.cs
-
-
- Common\UriQueryUtility.cs
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- True
- True
- Resources.resx
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
- Properties\CommonWebApiResources.Designer.cs
True
- True
CommonWebApiResources.resx
+ True
+ Properties\CommonWebApiResources.Designer.cs
-
-
- Properties\CommonWebApiResources.resx
ResXFileCodeGenerator
CommonWebApiResources.Designer.cs
+ Properties\CommonWebApiResources.resx
-
-
+
+
+ True
+ Resources.resx
+ True
+
ResXFileCodeGenerator
Resources.Designer.cs
Designer
-
-
-
- CodeAnalysisDictionary.xml
-
-
-
+
+
+
+
-
\ No newline at end of file
+
diff --git a/test/Microsoft.TestCommon/Microsoft.TestCommon.csproj b/test/Microsoft.TestCommon/Microsoft.TestCommon.csproj
index dae43e747..afb33a171 100644
--- a/test/Microsoft.TestCommon/Microsoft.TestCommon.csproj
+++ b/test/Microsoft.TestCommon/Microsoft.TestCommon.csproj
@@ -1,23 +1,30 @@
- netcoreapp2.1;net452
- ..\..\bin\$(Configuration)\Test\
+ net452;net462;netcoreapp2.1
$(Configurations);CodeAnalysis
+ $(DefineConstants);NETFX_CORE
false
+ ..\..\bin\$(Configuration)\Test\
+ $(OutputPath)NetCore\
+
-
+
+
+
-
+ Condition=" '$(TargetFrameworkIdentifier)' != '.NETFramework' " />
diff --git a/test/System.Net.Http.Formatting.NetCore.Test/System.Net.Http.Formatting.NetCore.Test.csproj b/test/System.Net.Http.Formatting.NetCore.Test/System.Net.Http.Formatting.NetCore.Test.csproj
index d60e9a90c..3c3ba3246 100644
--- a/test/System.Net.Http.Formatting.NetCore.Test/System.Net.Http.Formatting.NetCore.Test.csproj
+++ b/test/System.Net.Http.Formatting.NetCore.Test/System.Net.Http.Formatting.NetCore.Test.csproj
@@ -1,105 +1,36 @@
- net462
+ netcoreapp2.1;net462
System.Net.Http
- System.Net.Http.Formatting.NetCore.Test
..\..\bin\$(Configuration)\Test\NetCore\
$(Configurations);CodeAnalysis
false
$(DefineConstants);NETFX_CORE
true
+
+
all
runtime; build; native; contentfiles; analyzers
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ %(RecursiveDir)\%(Filename).cs
+
+
-
-
-
+
+
-
-
-
-
-
-
- This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
-
-
diff --git a/test/System.Net.Http.Formatting.NetStandard.Test/System.Net.Http.Formatting.NetStandard.Test.csproj b/test/System.Net.Http.Formatting.NetStandard.Test/System.Net.Http.Formatting.NetStandard.Test.csproj
index 64d06cf7e..716a01934 100644
--- a/test/System.Net.Http.Formatting.NetStandard.Test/System.Net.Http.Formatting.NetStandard.Test.csproj
+++ b/test/System.Net.Http.Formatting.NetStandard.Test/System.Net.Http.Formatting.NetStandard.Test.csproj
@@ -3,32 +3,31 @@
netcoreapp2.1;net462
System.Net.Http
- System.Net.Http.Formatting.NetStandard.Test
..\..\bin\$(Configuration)\Test\NetStandard\
$(Configurations);CodeAnalysis
false
true
+
+
all
runtime; build; native; contentfiles; analyzers
-
-
-
+
+
%(RecursiveDir)\%(Filename).cs
-
-
+
-
-
+
diff --git a/test/System.Net.Http.Formatting.Test/System.Net.Http.Formatting.Test.csproj b/test/System.Net.Http.Formatting.Test/System.Net.Http.Formatting.Test.csproj
index caec66151..c68e88c58 100644
--- a/test/System.Net.Http.Formatting.Test/System.Net.Http.Formatting.Test.csproj
+++ b/test/System.Net.Http.Formatting.Test/System.Net.Http.Formatting.Test.csproj
@@ -13,6 +13,7 @@
+
..\..\packages\Castle.Core.4.2.1\lib\net45\Castle.Core.dll
@@ -54,123 +55,13 @@
..\..\packages\xunit.extensibility.execution.2.3.0\lib\net452\xunit.execution.desktop.dll
True
-
-
+
False
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
{668E9021-CE84-49D9-98FB-DF125A9FCDB0}
System.Net.Http.Formatting
@@ -179,26 +70,28 @@
{FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0}
Microsoft.TestCommon
-
-
+
Designer
-
-
+
-
-
+
+
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
+
+
+
-
\ No newline at end of file
From 702846d5963924757da6acb6b80e831c5ce341e8 Mon Sep 17 00:00:00 2001
From: Doug Bunting <6431421+dougbu@users.noreply.github.com>
Date: Sun, 18 Dec 2022 22:03:51 -0800
Subject: [PATCH 02/19] React to using Newtonsoft.Json.Bson everywhere - remove
deprecated `BsonWriter` mentions entirely - remove legacy BSON writer test
workaround
---
.../Formatting/BsonMediaTypeFormatter.cs | 21 ++-----------------
.../Formatting/BsonMediaTypeFormatterTests.cs | 10 ++-------
2 files changed, 4 insertions(+), 27 deletions(-)
diff --git a/src/System.Net.Http.Formatting/Formatting/BsonMediaTypeFormatter.cs b/src/System.Net.Http.Formatting/Formatting/BsonMediaTypeFormatter.cs
index 99b8cca5b..ca19cd4c6 100644
--- a/src/System.Net.Http.Formatting/Formatting/BsonMediaTypeFormatter.cs
+++ b/src/System.Net.Http.Formatting/Formatting/BsonMediaTypeFormatter.cs
@@ -11,12 +11,7 @@
using System.Threading.Tasks;
using System.Web.Http;
using Newtonsoft.Json;
-#if NETFX_CORE
using Newtonsoft.Json.Bson;
-#else
-using BsonReader = Newtonsoft.Json.Bson.BsonDataReader;
-using BsonWriter = Newtonsoft.Json.Bson.BsonDataWriter;
-#endif
namespace System.Net.Http.Formatting
{
@@ -200,13 +195,7 @@ public override JsonReader CreateJsonReader(Type type, Stream readStream, Encodi
throw Error.ArgumentNull("effectiveEncoding");
}
-#if NETFX_CORE
-#pragma warning disable CS0618 // Type or member is obsolete
-#endif
- BsonReader reader = new BsonReader(new BinaryReader(readStream, effectiveEncoding));
-#if NETFX_CORE
-#pragma warning restore CS0618 // Type or member is obsolete
-#endif
+ BsonDataReader reader = new BsonDataReader(new BinaryReader(readStream, effectiveEncoding));
try
{
@@ -303,13 +292,7 @@ public override JsonWriter CreateJsonWriter(Type type, Stream writeStream, Encod
throw Error.ArgumentNull("effectiveEncoding");
}
-#if NETFX_CORE
-#pragma warning disable CS0618 // Type or member is obsolete
-#endif
- return new BsonWriter(new BinaryWriter(writeStream, effectiveEncoding));
-#if NETFX_CORE
-#pragma warning restore CS0618 // Type or member is obsolete
-#endif
+ return new BsonDataWriter(new BinaryWriter(writeStream, effectiveEncoding));
}
// Return true if Json.Net will likely convert value of given type to a Json primitive, not JsonArray nor
diff --git a/test/System.Net.Http.Formatting.Test/Formatting/BsonMediaTypeFormatterTests.cs b/test/System.Net.Http.Formatting.Test/Formatting/BsonMediaTypeFormatterTests.cs
index e28096df8..96fde3311 100644
--- a/test/System.Net.Http.Formatting.Test/Formatting/BsonMediaTypeFormatterTests.cs
+++ b/test/System.Net.Http.Formatting.Test/Formatting/BsonMediaTypeFormatterTests.cs
@@ -262,14 +262,8 @@ public async Task FormatterThrowsOnWriteWithInvalidContent()
BsonMediaTypeFormatter formatter = new BsonMediaTypeFormatter();
HttpContent content = new StringContent(String.Empty);
MemoryStream stream = new MemoryStream();
-#if NETFX_CORE // Separate Bson package (not yet used in NETCore project) calculates the path in exceptions differently
- string expectedPath = string.Empty;
-#else
- string expectedPath = "Value";
-#endif
- string expectedMessage = string.Format(
- "Value is too large to fit in a signed 32 bit integer. BSON does not support unsigned values. Path '{0}'.",
- expectedPath);
+ string expectedMessage =
+ "Value is too large to fit in a signed 32 bit integer. BSON does not support unsigned values. Path 'Value'.";
// Act & Assert
// Note error message is not quite correct: BSON supports byte, ushort, and smaller uint / ulong values.
From d53a5b06b79f2d42547b9b207316f6e1212e8a74 Mon Sep 17 00:00:00 2001
From: Doug Bunting <6431421+dougbu@users.noreply.github.com>
Date: Mon, 12 Dec 2022 18:00:03 -0800
Subject: [PATCH 03/19] Remove unnecessary `ConcurrentDictionary` class - was
not available in `Profile259` - leave tests to confirm correct operation w/
"real" class
---
.../Internal/ConcurrentDictionary.cs | 192 ---------------
.../Formatting/MediaTypeFormatter.cs | 5 -
.../Formatting/XmlMediaTypeFormatter.cs | 2 -
.../Internal/ConcurrentDictionaryTests.cs | 224 +-----------------
4 files changed, 1 insertion(+), 422 deletions(-)
delete mode 100644 src/System.Net.Http.Formatting.NetCore/Internal/ConcurrentDictionary.cs
diff --git a/src/System.Net.Http.Formatting.NetCore/Internal/ConcurrentDictionary.cs b/src/System.Net.Http.Formatting.NetCore/Internal/ConcurrentDictionary.cs
deleted file mode 100644
index 3df580802..000000000
--- a/src/System.Net.Http.Formatting.NetCore/Internal/ConcurrentDictionary.cs
+++ /dev/null
@@ -1,192 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-#if NETFX_CORE // This file should only be included by the NetCore version of the formatting project, but adding a guard here just in case.
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace System.Net.Http.Internal
-{
- // TODO: Remove this class after BCL makes their portable library version.
- internal sealed class ConcurrentDictionary : IDictionary
- {
- private Dictionary _dictionary = new Dictionary();
- private object _lock = new object();
-
- public ICollection Keys
- {
- get
- {
- throw new NotImplementedException();
- }
- }
-
- public ICollection Values
- {
- get
- {
- throw new NotImplementedException();
- }
- }
-
- public int Count
- {
- get
- {
- throw new NotImplementedException();
- }
- }
-
- public bool IsReadOnly
- {
- get
- {
- return ((IDictionary)_dictionary).IsReadOnly;
- }
- }
-
- public TValue this[TKey key]
- {
- get
- {
- throw new NotImplementedException();
- }
- set
- {
- throw new NotImplementedException();
- }
- }
-
- public void Add(TKey key, TValue value)
- {
- throw new NotImplementedException();
- }
-
- public bool ContainsKey(TKey key)
- {
- lock (_lock)
- {
- return _dictionary.ContainsKey(key);
- }
- }
-
- public bool Remove(TKey key)
- {
- throw new NotImplementedException();
- }
-
- public bool TryGetValue(TKey key, out TValue value)
- {
- lock (_lock)
- {
- return _dictionary.TryGetValue(key, out value);
- }
- }
-
- public void Add(KeyValuePair item)
- {
- throw new NotImplementedException();
- }
-
- public void Clear()
- {
- throw new NotImplementedException();
- }
-
- public bool Contains(KeyValuePair item)
- {
- throw new NotImplementedException();
- }
-
- public void CopyTo(KeyValuePair[] array, int arrayIndex)
- {
- throw new NotImplementedException();
- }
-
- public bool Remove(KeyValuePair item)
- {
- throw new NotImplementedException();
- }
-
- public IEnumerator> GetEnumerator()
- {
- throw new NotImplementedException();
- }
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- throw new NotImplementedException();
- }
-
- // ConcurrentDictionary members
- public bool TryRemove(TKey key, out TValue removedValue)
- {
- lock (_lock)
- {
- if (_dictionary.TryGetValue(key, out removedValue))
- {
- return _dictionary.Remove(key);
- }
-
- return false;
- }
- }
-
- public TValue GetOrAdd(TKey key, Func addValueFactory)
- {
- lock (_lock)
- {
- TValue value;
-
- if (!_dictionary.TryGetValue(key, out value))
- {
- value = addValueFactory.Invoke(key);
- _dictionary.Add(key, value);
- }
-
- return value;
- }
- }
-
- public bool TryAdd(TKey key, TValue value)
- {
- lock (_lock)
- {
- if (_dictionary.ContainsKey(key))
- {
- return false;
- }
-
- _dictionary.Add(key, value);
- return true;
- }
- }
-
- public TValue AddOrUpdate(TKey key, TValue addValue, Func updateValueFactory)
- {
- lock (_lock)
- {
- TValue value;
-
- // update
- if (_dictionary.TryGetValue(key, out value))
- {
- value = updateValueFactory.Invoke(key, value);
- _dictionary[key] = value;
- return value;
- }
-
- // add
- _dictionary.Add(key, addValue);
- return addValue;
- }
- }
- }
-}
-#endif
\ No newline at end of file
diff --git a/src/System.Net.Http.Formatting/Formatting/MediaTypeFormatter.cs b/src/System.Net.Http.Formatting/Formatting/MediaTypeFormatter.cs
index 367fba74a..1c6597380 100644
--- a/src/System.Net.Http.Formatting/Formatting/MediaTypeFormatter.cs
+++ b/src/System.Net.Http.Formatting/Formatting/MediaTypeFormatter.cs
@@ -1,17 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-#if !NETFX_CORE // In portable library we have our own implementation of Concurrent Dictionary which is in the internal namespace
using System.Collections.Concurrent;
-#endif
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Net.Http.Headers;
-#if NETFX_CORE // In portable library we have our own implementation of Concurrent Dictionary which is in the internal namespace
-using System.Net.Http.Internal;
-#endif
using System.Reflection;
using System.Text;
using System.Threading;
diff --git a/src/System.Net.Http.Formatting/Formatting/XmlMediaTypeFormatter.cs b/src/System.Net.Http.Formatting/Formatting/XmlMediaTypeFormatter.cs
index 2ffd75975..c0f8a0336 100644
--- a/src/System.Net.Http.Formatting/Formatting/XmlMediaTypeFormatter.cs
+++ b/src/System.Net.Http.Formatting/Formatting/XmlMediaTypeFormatter.cs
@@ -1,9 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-#if !NETFX_CORE // In portable library we have our own implementation of Concurrent Dictionary which is in the internal namespace
using System.Collections.Concurrent;
-#endif
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
diff --git a/test/System.Net.Http.Formatting.Test/Internal/ConcurrentDictionaryTests.cs b/test/System.Net.Http.Formatting.Test/Internal/ConcurrentDictionaryTests.cs
index 2294f8ff5..cc08f4d92 100644
--- a/test/System.Net.Http.Formatting.Test/Internal/ConcurrentDictionaryTests.cs
+++ b/test/System.Net.Http.Formatting.Test/Internal/ConcurrentDictionaryTests.cs
@@ -4,26 +4,10 @@
using System.Collections.Generic;
using Microsoft.TestCommon;
-#if NETFX_CORE
-namespace System.Net.Http.Internal
-#else
namespace System.Collections.Concurrent
-#endif
{
public class ConcurrentDictionaryTests
{
-#if NETFX_CORE // This doesn't exist on the ConcurrentDictionary in the full framework
- [Fact]
- public void IsReadOnly_ReturnsFalse()
- {
- // Arrange
- ConcurrentDictionary dictionary = new ConcurrentDictionary();
-
- // Act & Assert
- Assert.False(dictionary.IsReadOnly);
- }
-#endif
-
[Fact]
public void ContainsKey_ReturnsFalseWhenKeyIsNotPresent()
{
@@ -41,7 +25,7 @@ public void ContainsKey_ReturnsTrueWhenKeyIsPresent()
ConcurrentDictionary dictionary = new ConcurrentDictionary();
// Act
- dictionary.TryAdd(1, 2);
+ dictionary.TryAdd(1, 2);
// Assert
Assert.True(dictionary.ContainsKey(1));
@@ -129,211 +113,5 @@ public void AddOrUpdate_UpdatesValueWhenKeyIsPresent()
// Assert
Assert.Equal(3, result);
}
-
-#if NETFX_CORE
- [Fact]
- public void Add_ThrowsNotImplementedException()
- {
- // Arrange
- ConcurrentDictionary dictionary = new ConcurrentDictionary();
-
- // Act/Assert
- Assert.Throws(() => dictionary.Add(0, 0));
- }
-
- [Fact]
- public void Add_KeyValuePairThrowsNotImplementedException()
- {
- // Arrange
- ConcurrentDictionary dictionary = new ConcurrentDictionary();
-
- // Act/Assert
- Assert.Throws(() => dictionary.Add(new KeyValuePair(0, 0)));
- }
-
- [Fact]
- public void Clear_ThrowsNotImplementedException()
- {
- // Arrange
- ConcurrentDictionary dictionary = new ConcurrentDictionary();
-
- // Act/Assert
- Assert.Throws(() => dictionary.Clear());
- }
-
- [Fact]
- public void Contains_KeyValuePairThrowsNotImplementedException()
- {
- // Arrange
- ConcurrentDictionary dictionary = new ConcurrentDictionary();
-
- // Act/Assert
- Assert.Throws(() => dictionary.Contains(new KeyValuePair(0, 0)));
- }
-
- [Fact]
- public void CopyTo_ThrowsNotImplementedException()
- {
- // Arrange
- ConcurrentDictionary dictionary = new ConcurrentDictionary();
-
- // Act/Assert
- Assert.Throws(() => dictionary.CopyTo(new KeyValuePair[1], 1));
- }
-
- [Fact]
- public void GetEnumerator_ThrowsNotImplementedException()
- {
- // Arrange
- ConcurrentDictionary dictionary = new ConcurrentDictionary();
-
- // Act/Assert
- Assert.Throws(() => dictionary.GetEnumerator());
- }
-
- [Fact]
- public void Remove_ThrowsNotImplementedException()
- {
- // Arrange
- ConcurrentDictionary dictionary = new ConcurrentDictionary();
-
- // Act/Assert
- Assert.Throws(() => dictionary.Remove(0));
- }
-
- [Fact]
- public void Remove_WithKeyValuePairThrowsNotImplementedException()
- {
- // Arrange
- ConcurrentDictionary dictionary = new ConcurrentDictionary();
-
- // Act/Assert
- Assert.Throws(() => dictionary.Remove(new KeyValuePair(0, 0)));
- }
-
- [Fact]
- public void GetEnumerator_AsEnumerableThrowsNotImplementedException()
- {
- // Arrange
- ConcurrentDictionary dictionary = new ConcurrentDictionary();
-
- // Act/Assert
- Assert.Throws(() => ((System.Collections.IEnumerable)dictionary).GetEnumerator());
- }
-
- [Fact]
- public void TryGetValue_ReturnsTrueAndValueWhenPresent()
- {
- // Arrange
- ConcurrentDictionary dictionary = new ConcurrentDictionary();
- dictionary.TryAdd(1, -1);
-
- // Act
- int returnedValue;
- bool tryResult = dictionary.TryGetValue(1, out returnedValue);
-
- // Assert
- Assert.Equal(-1, returnedValue);
- Assert.True(tryResult);
- }
-
- [Fact]
- public void TryGetValue_ReturnsFalseAndDefaultWhenMissing()
- {
- // Arrange
- ConcurrentDictionary dictionary = new ConcurrentDictionary();
- dictionary.TryAdd(1, -1);
-
- // Act
- int returnedValue;
- bool tryResult = dictionary.TryGetValue(2, out returnedValue);
-
- // Assert
- Assert.Equal(0, returnedValue);
- Assert.False(tryResult);
- }
-
- [Fact]
- public void TryRemove_ReturnsTrueAndRemovesWhenPresent()
- {
- // Arrange
- ConcurrentDictionary dictionary = new ConcurrentDictionary();
- dictionary.TryAdd(1, -1);
-
- // Act
- int returnedValue;
- bool tryResult = dictionary.TryGetValue(1, out returnedValue);
-
- // Assert
- Assert.Equal(-1, returnedValue);
- Assert.True(tryResult);
- }
-
- [Fact]
- public void TryRemove_ReturnsFalseAndDefaultWhenMissing()
- {
- // Arrange
- ConcurrentDictionary dictionary = new ConcurrentDictionary();
- dictionary.TryAdd(1, -1);
-
- // Act
- int returnedValue;
- bool tryResult = dictionary.TryGetValue(2, out returnedValue);
-
- // Assert
- Assert.Equal(0, returnedValue);
- Assert.False(tryResult);
- }
-
- [Fact]
- public void Count_ThrowsNotImplementedException()
- {
- // Arrange
- ConcurrentDictionary dictionary = new ConcurrentDictionary();
-
- // Act/Assert
- Assert.Throws(() => dictionary.Count);
- }
-
- [Fact]
- public void GetItem_ThrowsNotImplementedException()
- {
- // Arrange
- ConcurrentDictionary dictionary = new ConcurrentDictionary();
-
- // Act/Assert
- Assert.Throws(() => dictionary[0]);
- }
-
- [Fact]
- public void SetItem_ThrowsNotImplementedException()
- {
- // Arrange
- ConcurrentDictionary dictionary = new ConcurrentDictionary();
-
- // Act/Assert
- Assert.Throws(() => dictionary[0] = 1);
- }
-
- [Fact]
- public void GetKeys_ThrowsNotImplementedException()
- {
- // Arrange
- ConcurrentDictionary dictionary = new ConcurrentDictionary();
-
- // Act/Assert
- Assert.Throws(() => dictionary.Keys);
- }
-
- [Fact]
- public void GetValues_ThrowsNotImplementedException()
- {
- // Arrange
- ConcurrentDictionary dictionary = new ConcurrentDictionary();
-
- // Act/Assert
- Assert.Throws(() => dictionary.Values);
- }
-#endif
}
}
From 86fdf4c2f664606d37bde025052c08b8075598ad Mon Sep 17 00:00:00 2001
From: Doug Bunting <6431421+dougbu@users.noreply.github.com>
Date: Fri, 25 Nov 2022 14:01:23 -0800
Subject: [PATCH 04/19] Remove `UriQueryUtility` because `WebUtility` is
available everywhere - leave test to confirm `WebUtility` continues to behave
about the same - handle `WebUtility` being a `public` class - change other
tests to handle `WebUtility.UrlEncode(...)` encoding to uppercase - unlike
`UriQueryUtility.UrlEncode(...)`
---
src/Common/UriQueryUtility.cs | 278 ------------------
.../Parsers/FormUrlEncodedParser.cs | 6 +-
.../Internal/HttpValueCollection.cs | 4 +-
.../Settings.StyleCop | 10 -
.../System.Net.Http.Formatting.csproj | 1 -
src/System.Web.Http/Settings.StyleCop | 11 -
src/System.Web.Http/System.Web.Http.csproj | 3 -
test/Common/UriQueryUtilityTest.cs | 9 +-
.../FormUrlEncodedFromContentTests.cs | 4 +-
.../FormUrlEncodedFromUriQueryTests.cs | 4 +-
.../Headers/CookieStateTest.cs | 22 +-
.../HttpRequestHeadersExtensionsTest.cs | 2 +-
.../Internal/HttpValueCollectionTest.cs | 12 -
13 files changed, 27 insertions(+), 339 deletions(-)
delete mode 100644 src/Common/UriQueryUtility.cs
delete mode 100644 src/System.Net.Http.Formatting/Settings.StyleCop
delete mode 100644 src/System.Web.Http/Settings.StyleCop
diff --git a/src/Common/UriQueryUtility.cs b/src/Common/UriQueryUtility.cs
deleted file mode 100644
index 53675e11a..000000000
--- a/src/Common/UriQueryUtility.cs
+++ /dev/null
@@ -1,278 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Diagnostics.Contracts;
-using System.Net;
-using System.Text;
-
-namespace System.Web.Http
-{
- ///
- /// Helpers for encoding, decoding, and parsing URI query components. In .Net 4.5
- /// please use the WebUtility class.
- ///
- internal static class UriQueryUtility
- {
- public static string UrlEncode(string str)
- {
- if (str == null)
- {
- return null;
- }
-
-#if NETFX_CORE
- return WebUtility.UrlEncode(str);
-#else
- byte[] bytes = Encoding.UTF8.GetBytes(str);
- return Encoding.ASCII.GetString(UrlEncode(bytes, 0, bytes.Length, alwaysCreateNewReturnValue: false));
-#endif
- }
-
- public static string UrlDecode(string str)
- {
- if (str == null)
- {
- return null;
- }
-
-#if NETFX_CORE
- return WebUtility.UrlDecode(str);
-#else
- return UrlDecodeInternal(str, Encoding.UTF8);
-#endif
- }
-
-#if !NETFX_CORE
- private static byte[] UrlEncode(byte[] bytes, int offset, int count, bool alwaysCreateNewReturnValue)
- {
- byte[] encoded = UrlEncode(bytes, offset, count);
-
- return (alwaysCreateNewReturnValue && (encoded != null) && (encoded == bytes))
- ? (byte[])encoded.Clone()
- : encoded;
- }
-
- private static byte[] UrlEncode(byte[] bytes, int offset, int count)
- {
- if (!ValidateUrlEncodingParameters(bytes, offset, count))
- {
- return null;
- }
-
- int cSpaces = 0;
- int cUnsafe = 0;
-
- // count them first
- for (int i = 0; i < count; i++)
- {
- char ch = (char)bytes[offset + i];
-
- if (ch == ' ')
- cSpaces++;
- else if (!IsUrlSafeChar(ch))
- cUnsafe++;
- }
-
- // nothing to expand?
- if (cSpaces == 0 && cUnsafe == 0)
- return bytes;
-
- // expand not 'safe' characters into %XX, spaces to +s
- byte[] expandedBytes = new byte[count + cUnsafe * 2];
- int pos = 0;
-
- for (int i = 0; i < count; i++)
- {
- byte b = bytes[offset + i];
- char ch = (char)b;
-
- if (IsUrlSafeChar(ch))
- {
- expandedBytes[pos++] = b;
- }
- else if (ch == ' ')
- {
- expandedBytes[pos++] = (byte)'+';
- }
- else
- {
- expandedBytes[pos++] = (byte)'%';
- expandedBytes[pos++] = (byte)IntToHex((b >> 4) & 0xf);
- expandedBytes[pos++] = (byte)IntToHex(b & 0x0f);
- }
- }
-
- return expandedBytes;
- }
-
- private static string UrlDecodeInternal(string value, Encoding encoding)
- {
- if (value == null)
- {
- return null;
- }
-
- int count = value.Length;
- UrlDecoder helper = new UrlDecoder(count, encoding);
-
- // go through the string's chars collapsing %XX and %uXXXX and
- // appending each char as char, with exception of %XX constructs
- // that are appended as bytes
-
- for (int pos = 0; pos < count; pos++)
- {
- char ch = value[pos];
-
- if (ch == '+')
- {
- ch = ' ';
- }
- else if (ch == '%' && pos < count - 2)
- {
- int h1 = HexToInt(value[pos + 1]);
- int h2 = HexToInt(value[pos + 2]);
-
- if (h1 >= 0 && h2 >= 0)
- {
- // valid 2 hex chars
- byte b = (byte)((h1 << 4) | h2);
- pos += 2;
-
- // don't add as char
- helper.AddByte(b);
- continue;
- }
- }
-
- if ((ch & 0xFF80) == 0)
- helper.AddByte((byte)ch); // 7 bit have to go as bytes because of Unicode
- else
- helper.AddChar(ch);
- }
-
- return helper.GetString();
- }
-
- private static int HexToInt(char h)
- {
- return (h >= '0' && h <= '9') ? h - '0' :
- (h >= 'a' && h <= 'f') ? h - 'a' + 10 :
- (h >= 'A' && h <= 'F') ? h - 'A' + 10 :
- -1;
- }
-
- private static char IntToHex(int n)
- {
- Contract.Assert(n < 0x10);
-
- if (n <= 9)
- return (char)(n + (int)'0');
- else
- return (char)(n - 10 + (int)'a');
- }
-
- // Set of safe chars, from RFC 1738.4 minus '+'
- private static bool IsUrlSafeChar(char ch)
- {
- if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch >= '0' && ch <= '9')
- return true;
-
- switch (ch)
- {
- case '-':
- case '_':
- case '.':
- case '!':
- case '*':
- case '(':
- case ')':
- return true;
- }
-
- return false;
- }
-
- private static bool ValidateUrlEncodingParameters(byte[] bytes, int offset, int count)
- {
- if (bytes == null && count == 0)
- return false;
- if (bytes == null)
- {
- throw Error.ArgumentNull("bytes");
- }
- if (offset < 0 || offset > bytes.Length)
- {
- throw new ArgumentOutOfRangeException("offset");
- }
- if (count < 0 || offset + count > bytes.Length)
- {
- throw new ArgumentOutOfRangeException("count");
- }
-
- return true;
- }
-
- // Internal class to facilitate URL decoding -- keeps char buffer and byte buffer, allows appending of either chars or bytes
- private class UrlDecoder
- {
- private int _bufferSize;
-
- // Accumulate characters in a special array
- private int _numChars;
- private char[] _charBuffer;
-
- // Accumulate bytes for decoding into characters in a special array
- private int _numBytes;
- private byte[] _byteBuffer;
-
- // Encoding to convert chars to bytes
- private Encoding _encoding;
-
- private void FlushBytes()
- {
- if (_numBytes > 0)
- {
- _numChars += _encoding.GetChars(_byteBuffer, 0, _numBytes, _charBuffer, _numChars);
- _numBytes = 0;
- }
- }
-
- internal UrlDecoder(int bufferSize, Encoding encoding)
- {
- _bufferSize = bufferSize;
- _encoding = encoding;
-
- _charBuffer = new char[bufferSize];
- // byte buffer created on demand
- }
-
- internal void AddChar(char ch)
- {
- if (_numBytes > 0)
- FlushBytes();
-
- _charBuffer[_numChars++] = ch;
- }
-
- internal void AddByte(byte b)
- {
- if (_byteBuffer == null)
- _byteBuffer = new byte[_bufferSize];
-
- _byteBuffer[_numBytes++] = b;
- }
-
- internal String GetString()
- {
- if (_numBytes > 0)
- FlushBytes();
-
- if (_numChars > 0)
- return new String(_charBuffer, 0, _numChars);
- else
- return String.Empty;
- }
- }
-#endif
- }
-}
\ No newline at end of file
diff --git a/src/System.Net.Http.Formatting/Formatting/Parsers/FormUrlEncodedParser.cs b/src/System.Net.Http.Formatting/Formatting/Parsers/FormUrlEncodedParser.cs
index 6a4f42024..90754c123 100644
--- a/src/System.Net.Http.Formatting/Formatting/Parsers/FormUrlEncodedParser.cs
+++ b/src/System.Net.Http.Formatting/Formatting/Parsers/FormUrlEncodedParser.cs
@@ -275,9 +275,9 @@ public StringBuilder Value
/// The collection to copy into.
public void CopyTo(ICollection> nameValuePairs)
{
- string unescapedName = UriQueryUtility.UrlDecode(_name.ToString());
+ string unescapedName = WebUtility.UrlDecode(_name.ToString());
string escapedValue = _value.ToString();
- string value = UriQueryUtility.UrlDecode(escapedValue);
+ string value = WebUtility.UrlDecode(escapedValue);
nameValuePairs.Add(new KeyValuePair(unescapedName, value));
@@ -290,7 +290,7 @@ public void CopyTo(ICollection> nameValuePairs)
/// The collection to copy into.
public void CopyNameOnlyTo(ICollection> nameValuePairs)
{
- string unescapedName = UriQueryUtility.UrlDecode(_name.ToString());
+ string unescapedName = WebUtility.UrlDecode(_name.ToString());
string value = String.Empty;
nameValuePairs.Add(new KeyValuePair(unescapedName, value));
Clear();
diff --git a/src/System.Net.Http.Formatting/Internal/HttpValueCollection.cs b/src/System.Net.Http.Formatting/Internal/HttpValueCollection.cs
index 3197a9733..85ec3cd4e 100644
--- a/src/System.Net.Http.Formatting/Internal/HttpValueCollection.cs
+++ b/src/System.Net.Http.Formatting/Internal/HttpValueCollection.cs
@@ -157,10 +157,10 @@ private string ToString(bool urlEncode)
private static bool AppendNameValuePair(StringBuilder builder, bool first, bool urlEncode, string name, string value)
{
string effectiveName = name ?? String.Empty;
- string encodedName = urlEncode ? UriQueryUtility.UrlEncode(effectiveName) : effectiveName;
+ string encodedName = urlEncode ? WebUtility.UrlEncode(effectiveName) : effectiveName;
string effectiveValue = value ?? String.Empty;
- string encodedValue = urlEncode ? UriQueryUtility.UrlEncode(effectiveValue) : effectiveValue;
+ string encodedValue = urlEncode ? WebUtility.UrlEncode(effectiveValue) : effectiveValue;
if (first)
{
diff --git a/src/System.Net.Http.Formatting/Settings.StyleCop b/src/System.Net.Http.Formatting/Settings.StyleCop
deleted file mode 100644
index 5b387864d..000000000
--- a/src/System.Net.Http.Formatting/Settings.StyleCop
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- UriQueryUtility.cs
-
-
- False
-
-
-
-
\ No newline at end of file
diff --git a/src/System.Net.Http.Formatting/System.Net.Http.Formatting.csproj b/src/System.Net.Http.Formatting/System.Net.Http.Formatting.csproj
index d871af3af..d414f2c1f 100644
--- a/src/System.Net.Http.Formatting/System.Net.Http.Formatting.csproj
+++ b/src/System.Net.Http.Formatting/System.Net.Http.Formatting.csproj
@@ -42,7 +42,6 @@
-
diff --git a/src/System.Web.Http/Settings.StyleCop b/src/System.Web.Http/Settings.StyleCop
deleted file mode 100644
index ddf8ec764..000000000
--- a/src/System.Web.Http/Settings.StyleCop
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
- DynamicQueryable.cs
- UriQueryUtility.cs
-
-
- False
-
-
-
-
\ No newline at end of file
diff --git a/src/System.Web.Http/System.Web.Http.csproj b/src/System.Web.Http/System.Web.Http.csproj
index eaa991282..27041a371 100644
--- a/src/System.Web.Http/System.Web.Http.csproj
+++ b/src/System.Web.Http/System.Web.Http.csproj
@@ -102,9 +102,6 @@
Common\TypeExtensions.cs
-
- Common\UriQueryUtility.cs
-
diff --git a/test/Common/UriQueryUtilityTest.cs b/test/Common/UriQueryUtilityTest.cs
index 4b325abbd..ac0307cae 100644
--- a/test/Common/UriQueryUtilityTest.cs
+++ b/test/Common/UriQueryUtilityTest.cs
@@ -10,7 +10,7 @@
namespace System.Net.Http
{
- public class UriQueryUtilityTest
+ public class WebUtilityTest
{
public static TheoryDataSet UriQueryData
{
@@ -23,19 +23,20 @@ public static TheoryDataSet UriQueryData
[Fact]
public void TypeIsCorrect()
{
- Assert.Type.HasProperties(typeof(UriQueryUtility), TypeAssert.TypeProperties.IsClass | TypeAssert.TypeProperties.IsStatic);
+ Assert.Type.HasProperties(typeof(WebUtility),
+ TypeAssert.TypeProperties.IsStatic | TypeAssert.TypeProperties.IsPublicVisibleClass);
}
[Fact]
public void UrlEncode_ReturnsNull()
{
- Assert.Null(UriQueryUtility.UrlEncode(null));
+ Assert.Null(WebUtility.UrlEncode(null));
}
[Fact]
public void UrlDecode_ReturnsNull()
{
- Assert.Null(UriQueryUtility.UrlDecode(null));
+ Assert.Null(WebUtility.UrlDecode(null));
}
[Fact]
diff --git a/test/System.Net.Http.Formatting.Test/Formatting/FormUrlEncodedFromContentTests.cs b/test/System.Net.Http.Formatting.Test/Formatting/FormUrlEncodedFromContentTests.cs
index dec45eab4..fcc01232d 100644
--- a/test/System.Net.Http.Formatting.Test/Formatting/FormUrlEncodedFromContentTests.cs
+++ b/test/System.Net.Http.Formatting.Test/Formatting/FormUrlEncodedFromContentTests.cs
@@ -340,11 +340,11 @@ private static void BuildParams(string prefix, JToken jsonValue, List re
{
dateStr = dateStr.Substring(1, dateStr.Length - 2);
}
- results.Add(prefix + "=" + UriQueryUtility.UrlEncode(dateStr));
+ results.Add(prefix + "=" + WebUtility.UrlEncode(dateStr));
}
else
{
- results.Add(prefix + "=" + UriQueryUtility.UrlEncode(jsonPrimitive.Value.ToString()));
+ results.Add(prefix + "=" + WebUtility.UrlEncode(jsonPrimitive.Value.ToString()));
}
}
}
diff --git a/test/System.Net.Http.Formatting.Test/Formatting/FormUrlEncodedFromUriQueryTests.cs b/test/System.Net.Http.Formatting.Test/Formatting/FormUrlEncodedFromUriQueryTests.cs
index 4dee5366e..b56717039 100644
--- a/test/System.Net.Http.Formatting.Test/Formatting/FormUrlEncodedFromUriQueryTests.cs
+++ b/test/System.Net.Http.Formatting.Test/Formatting/FormUrlEncodedFromUriQueryTests.cs
@@ -279,11 +279,11 @@ private static void BuildParams(string prefix, JToken jsonValue, List re
{
dateStr = dateStr.Substring(1, dateStr.Length - 2);
}
- results.Add(prefix + "=" + UriQueryUtility.UrlEncode(dateStr));
+ results.Add(prefix + "=" + WebUtility.UrlEncode(dateStr));
}
else
{
- results.Add(prefix + "=" + UriQueryUtility.UrlEncode(jsonPrimitive.Value.ToString()));
+ results.Add(prefix + "=" + WebUtility.UrlEncode(jsonPrimitive.Value.ToString()));
}
}
}
diff --git a/test/System.Net.Http.Formatting.Test/Headers/CookieStateTest.cs b/test/System.Net.Http.Formatting.Test/Headers/CookieStateTest.cs
index a00af171c..68383c238 100644
--- a/test/System.Net.Http.Formatting.Test/Headers/CookieStateTest.cs
+++ b/test/System.Net.Http.Formatting.Test/Headers/CookieStateTest.cs
@@ -29,19 +29,21 @@ public static TheoryDataSet EncodedCookieStateStrings
{
get
{
- return new TheoryDataSet
+ TheoryDataSet data = new TheoryDataSet
{
- { "?", "%3f" },
- { "=", "%3d" },
- { "", "%3cacb%3e" },
- { "{acb}", "%7bacb%7d" },
- { "[acb]", "%5bacb%5d" },
+ { "?", "%3F" },
+ { "=", "%3D" },
+ { "", "%3Cacb%3E" },
+ { "{acb}", "%7Bacb%7D" },
+ { "[acb]", "%5Bacb%5D" },
{ "\"acb\"", "%22acb%22" },
- { "a,b", "a%2cb" },
- { "a;b", "a%3bb" },
- { "a\\b", "a%5cb" },
- { "[]{}\\|!@#$%^&*()_-+=", "%5b%5d%7b%7d%5c%7c!%40%23%24%25%5e%26*()_-%2b%3d" },
+ { "a,b", "a%2Cb" },
+ { "a;b", "a%3Bb" },
+ { "a\\b", "a%5Cb" },
+ { "[]{}\\|!@#$%^&*()_-+=", "%5B%5D%7B%7D%5C%7C!%40%23%24%25%5E%26*()_-%2B%3D" },
};
+
+ return data;
}
}
diff --git a/test/System.Net.Http.Formatting.Test/HttpRequestHeadersExtensionsTest.cs b/test/System.Net.Http.Formatting.Test/HttpRequestHeadersExtensionsTest.cs
index a02eaa6da..b385cb423 100644
--- a/test/System.Net.Http.Formatting.Test/HttpRequestHeadersExtensionsTest.cs
+++ b/test/System.Net.Http.Formatting.Test/HttpRequestHeadersExtensionsTest.cs
@@ -45,7 +45,7 @@ public static TheoryDataSet CookieMatches
new string[]
{
"adxcs=-",
- "ADXCS=si=0%3a1"
+ "ADXCS=si=0%3A1"
}
},
{
diff --git a/test/System.Net.Http.Formatting.Test/Internal/HttpValueCollectionTest.cs b/test/System.Net.Http.Formatting.Test/Internal/HttpValueCollectionTest.cs
index 03b5aadf0..08ff25744 100644
--- a/test/System.Net.Http.Formatting.Test/Internal/HttpValueCollectionTest.cs
+++ b/test/System.Net.Http.Formatting.Test/Internal/HttpValueCollectionTest.cs
@@ -79,11 +79,7 @@ public static TheoryDataSet ToStringTestData
hvc4.Add("na me", "");
dataSet.Add(hvc4, "na+me");
-#if NETFX_CORE
string encoded5 = "n%22%2C%3B%5Cn";
-#else
- string encoded5 = "n%22%2c%3b%5cn";
-#endif
var hvc5 = CreateInstance();
hvc5.Add("n\",;\\n", "");
@@ -103,22 +99,14 @@ public static TheoryDataSet ToStringTestData
hvc7.Add("n4", "v4");
dataSet.Add(hvc7, "n1=v1&n2=v2&n3=v3&n4=v4");
-#if NETFX_CORE
string encoded8 = "n%2C1=v%2C1&n%3B2=v%3B2";
-#else
- string encoded8 = "n%2c1=v%2c1&n%3b2=v%3b2";
-#endif
var hvc8 = CreateInstance();
hvc8.Add("n,1", "v,1");
hvc8.Add("n;2", "v;2");
dataSet.Add(hvc8, encoded8);
-#if NETFX_CORE
string encoded9 = "n1=%26&n2=%3B&n3=%26&n4=%2B&n5=%26&n6=%3D&n7=%26";
-#else
- string encoded9 = "n1=%26&n2=%3b&n3=%26&n4=%2b&n5=%26&n6=%3d&n7=%26";
-#endif
var hvc9 = CreateInstance();
hvc9.Add("n1", "&");
From 84d7ed733ea8001daee2f05d49a5eff1f38ddf09 Mon Sep 17 00:00:00 2001
From: Doug Bunting <6431421+dougbu@users.noreply.github.com>
Date: Fri, 25 Nov 2022 14:11:18 -0800
Subject: [PATCH 05/19] Add simple classes to NetCore project - use
`ICloneable` copy to get more classes compiling - helps NetCore in similar
way to existing `MediaTypeHeaderValueExtensions` - use
`InvalidEnumArgumentException` copy to simplify code and testing - both not
available in `netstandard1.3` (or available packages) but easily duplicated
---
src/Common/Error.cs | 23 +++++++++++++++----
.../ICloneable.cs | 9 ++++++++
.../Properties/AssemblyInfo.cs | 1 +
.../ExceptionAssertions.cs | 10 +++++++-
.../Microsoft.TestCommon.csproj | 4 ++++
.../Formatting/StringComparisonHelperTest.cs | 10 +-------
6 files changed, 43 insertions(+), 14 deletions(-)
create mode 100644 src/System.Net.Http.Formatting.NetCore/ICloneable.cs
diff --git a/src/Common/Error.cs b/src/Common/Error.cs
index b6013c5dc..f25b3d9b2 100644
--- a/src/Common/Error.cs
+++ b/src/Common/Error.cs
@@ -225,11 +225,7 @@ internal static OperationCanceledException OperationCanceled(string messageForma
/// The logged .
internal static ArgumentException InvalidEnumArgument(string parameterName, int invalidValue, Type enumClass)
{
-#if NETFX_CORE
- return new ArgumentException(Error.Format(CommonWebApiResources.InvalidEnumArgument, parameterName, invalidValue, enumClass.Name), parameterName);
-#else
return new InvalidEnumArgumentException(parameterName, invalidValue, enumClass);
-#endif
}
///
@@ -265,5 +261,24 @@ internal static NotSupportedException NotSupported(string messageFormat, params
{
return new NotSupportedException(Error.Format(messageFormat, messageArgs));
}
+
+#if NETFX_CORE // InvalidEnumArgumentException not available in netstandard1.3.
+ internal class InvalidEnumArgumentException : ArgumentException
+ {
+ public InvalidEnumArgumentException() : this(null)
+ { }
+
+ public InvalidEnumArgumentException(string message) : base(message)
+ { }
+
+ public InvalidEnumArgumentException(string message, Exception innerException) : base(message, innerException)
+ { }
+
+ public InvalidEnumArgumentException(string argumentName, int invalidValue, Type enumClass) : base(
+ Error.Format(CommonWebApiResources.InvalidEnumArgument, argumentName, invalidValue, enumClass.Name),
+ argumentName)
+ { }
+ }
+#endif
}
}
diff --git a/src/System.Net.Http.Formatting.NetCore/ICloneable.cs b/src/System.Net.Http.Formatting.NetCore/ICloneable.cs
new file mode 100644
index 000000000..1fc5b88c5
--- /dev/null
+++ b/src/System.Net.Http.Formatting.NetCore/ICloneable.cs
@@ -0,0 +1,9 @@
+// No ICloneable interface in .NET Standard 1.3.
+
+namespace System
+{
+ internal interface ICloneable
+ {
+ object Clone();
+ }
+}
diff --git a/src/System.Net.Http.Formatting/Properties/AssemblyInfo.cs b/src/System.Net.Http.Formatting/Properties/AssemblyInfo.cs
index 454c8861c..ca360a811 100644
--- a/src/System.Net.Http.Formatting/Properties/AssemblyInfo.cs
+++ b/src/System.Net.Http.Formatting/Properties/AssemblyInfo.cs
@@ -21,6 +21,7 @@
#if NETSTANDARD2_0
[assembly: InternalsVisibleTo("System.Net.Http.Formatting.NetStandard.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
#elif NETFX_CORE
+[assembly: InternalsVisibleTo("Microsoft.TestCommon, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
[assembly: InternalsVisibleTo("System.Net.Http.Formatting.NetCore.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
#else
[assembly: InternalsVisibleTo("System.Net.Http.Formatting.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
diff --git a/test/Microsoft.TestCommon/ExceptionAssertions.cs b/test/Microsoft.TestCommon/ExceptionAssertions.cs
index 11b36e9e6..5cca7e7c8 100644
--- a/test/Microsoft.TestCommon/ExceptionAssertions.cs
+++ b/test/Microsoft.TestCommon/ExceptionAssertions.cs
@@ -6,6 +6,9 @@
using System.Reflection;
using System.Threading.Tasks;
using System.Web;
+#if NETFX_CORE
+using System.Web.Http;
+#endif
namespace Microsoft.TestCommon
{
@@ -494,12 +497,17 @@ public static HttpException ThrowsHttpException(Action testCode, string exceptio
/// Pass true to allow exceptions which derive from TException; pass false, otherwise
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
- public static InvalidEnumArgumentException ThrowsInvalidEnumArgument(Action testCode, string paramName, int invalidValue, Type enumType, bool allowDerivedExceptions = false)
+ public static ArgumentException ThrowsInvalidEnumArgument(Action testCode, string paramName, int invalidValue, Type enumType, bool allowDerivedExceptions = false)
{
string message = String.Format(CultureReplacer.DefaultCulture,
"The value of argument '{0}' ({1}) is invalid for Enum type '{2}'.{3}Parameter name: {0}",
paramName, invalidValue, enumType.Name, Environment.NewLine);
+
+#if NETFX_CORE // InvalidEnumArgumentException not available in netstandard1.3.
+ return Throws(testCode, message, allowDerivedExceptions);
+#else
return Throws(testCode, message, allowDerivedExceptions);
+#endif
}
///
diff --git a/test/Microsoft.TestCommon/Microsoft.TestCommon.csproj b/test/Microsoft.TestCommon/Microsoft.TestCommon.csproj
index afb33a171..4def411d9 100644
--- a/test/Microsoft.TestCommon/Microsoft.TestCommon.csproj
+++ b/test/Microsoft.TestCommon/Microsoft.TestCommon.csproj
@@ -26,5 +26,9 @@
+
+
+
diff --git a/test/System.Net.Http.Formatting.Test/Formatting/StringComparisonHelperTest.cs b/test/System.Net.Http.Formatting.Test/Formatting/StringComparisonHelperTest.cs
index 77a8d2361..f18764086 100644
--- a/test/System.Net.Http.Formatting.Test/Formatting/StringComparisonHelperTest.cs
+++ b/test/System.Net.Http.Formatting.Test/Formatting/StringComparisonHelperTest.cs
@@ -12,15 +12,7 @@ public StringComparisonHelperTest()
{
}
-#if NETFX_CORE // InvariantCulture and InvarianteCultureIgnore case are not supported in portable library projects
- protected override void AssertForUndefinedValue(Action testCode, string parameterName, int invalidValue, Type enumType, bool allowDerivedExceptions = false)
- {
- Assert.ThrowsArgument(
- testCode,
- parameterName,
- allowDerivedExceptions);
- }
-
+#if NETFX_CORE // InvariantCulture and InvariantCultureIgnoreCase case are not supported in portable library projects
protected override bool ValueExistsForFramework(StringComparison value)
{
return !(value == StringComparison.InvariantCulture || value == StringComparison.InvariantCultureIgnoreCase);
From 163f187eac85ef1cd5b16dd6b19d7468d3f1676f Mon Sep 17 00:00:00 2001
From: Doug Bunting <6431421+dougbu@users.noreply.github.com>
Date: Fri, 25 Nov 2022 14:14:27 -0800
Subject: [PATCH 06/19] Remove unnecessary NetCore-specific
GlobalSuppressions.cs file
---
.../GlobalSuppressions.cs | 10 ----------
1 file changed, 10 deletions(-)
delete mode 100644 src/System.Net.Http.Formatting.NetCore/GlobalSuppressions.cs
diff --git a/src/System.Net.Http.Formatting.NetCore/GlobalSuppressions.cs b/src/System.Net.Http.Formatting.NetCore/GlobalSuppressions.cs
deleted file mode 100644
index 778d614cd..000000000
--- a/src/System.Net.Http.Formatting.NetCore/GlobalSuppressions.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Diagnostics.CodeAnalysis;
-
-[assembly: SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames")]
-[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Net.Http.Headers", Justification = "We follow the layout of System.Net.Http.")]
-[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Net.Http.Handlers", Justification = "Handlers provide an extensibility hook which we want to keep in a separate namespace.")]
-[assembly: SuppressMessage("Microsoft.Design", "CA1014:MarkAssembliesWithClsCompliant", Scope = "module", Target = "system.net.http.formatting.dll", Justification = "CLSCompliant is not applicable to the portable version of the assembly.")]
-[assembly: SuppressMessage("Microsoft.Web.FxCop", "MW1000:UnusedResourceUsageRule", Scope = "module", Target = "system.net.http.formatting.dll", Justification = "The resources are only used in the non-portable version of the assembly.")]
From d14845bc5a2296a46f25b502ab44ed92c23124c0 Mon Sep 17 00:00:00 2001
From: Doug Bunting <6431421+dougbu@users.noreply.github.com>
Date: Fri, 25 Nov 2022 16:02:05 -0800
Subject: [PATCH 07/19] Use `NameValueCollection` everywhere - available (but
not serializable) in `netstandard1.3`
---
.../Formatting/FormDataCollection.cs | 13 +-
.../HttpContentFormDataExtensions.cs | 3 -
.../Internal/HttpValueCollection.cs | 151 +-----------------
.../UriExtensions.cs | 9 --
.../HttpContentFormDataExtensionsTest.cs | 3 -
.../Internal/HttpValueCollectionTest.cs | 12 --
6 files changed, 8 insertions(+), 183 deletions(-)
diff --git a/src/System.Net.Http.Formatting/Formatting/FormDataCollection.cs b/src/System.Net.Http.Formatting/Formatting/FormDataCollection.cs
index 17ce38c8e..74a5ea549 100644
--- a/src/System.Net.Http.Formatting/Formatting/FormDataCollection.cs
+++ b/src/System.Net.Http.Formatting/Formatting/FormDataCollection.cs
@@ -5,18 +5,12 @@
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
-#if !NETFX_CORE
using System.Net.Http.Formatting.Internal;
-#endif
using System.Net.Http.Formatting.Parsers;
using System.Text;
using System.Threading;
using System.Web.Http;
-#if NETFX_CORE
-using NameValueCollection = System.Net.Http.Formatting.HttpValueCollection;
-#endif
-
namespace System.Net.Http.Formatting
{
///
@@ -25,12 +19,7 @@ namespace System.Net.Http.Formatting
/// - using interfaces allows us to optimize the implementation. E.g., we can avoid eagerly string-splitting a 10gb file.
/// - This also provides a convenient place to put extension methods.
///
-#if NETFX_CORE
- internal
-#else
- public
-#endif
- class FormDataCollection : IEnumerable>
+ public class FormDataCollection : IEnumerable>
{
private readonly IEnumerable> _pairs;
diff --git a/src/System.Net.Http.Formatting/HttpContentFormDataExtensions.cs b/src/System.Net.Http.Formatting/HttpContentFormDataExtensions.cs
index 4a830684d..72ebb40c7 100644
--- a/src/System.Net.Http.Formatting/HttpContentFormDataExtensions.cs
+++ b/src/System.Net.Http.Formatting/HttpContentFormDataExtensions.cs
@@ -8,9 +8,6 @@
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
-#if NETFX_CORE
-using NameValueCollection = System.Net.Http.Formatting.HttpValueCollection;
-#endif
namespace System.Net.Http
{
diff --git a/src/System.Net.Http.Formatting/Internal/HttpValueCollection.cs b/src/System.Net.Http.Formatting/Internal/HttpValueCollection.cs
index 85ec3cd4e..72f28d4cd 100644
--- a/src/System.Net.Http.Formatting/Internal/HttpValueCollection.cs
+++ b/src/System.Net.Http.Formatting/Internal/HttpValueCollection.cs
@@ -1,14 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-#if NETFX_CORE
-using System.Collections;
-using System.Collections.Generic;
-using System.Diagnostics.Contracts;
-using System.Linq;
-using System.Text;
-using System.Web.Http;
-#else
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics.Contracts;
@@ -17,45 +9,29 @@
using System.Runtime.Serialization;
using System.Text;
using System.Web.Http;
-#endif
-#if NETFX_CORE
-namespace System.Net.Http.Formatting
-#else
namespace System.Net.Http.Formatting.Internal
-#endif
{
///
/// NameValueCollection to represent form data and to generate form data output.
///
-#if NETFX_CORE
- public class HttpValueCollection : IEnumerable>
-#else
+#if !NETFX_CORE // NameValueCollection is not serializable in netstandard1.3.
[Serializable]
- internal class HttpValueCollection : NameValueCollection
#endif
+ internal class HttpValueCollection : NameValueCollection
{
-#if NETFX_CORE
- internal readonly HashSet Names = new HashSet(StringComparer.OrdinalIgnoreCase);
- internal readonly List> List = new List>();
-
- ///
- /// Creates a new instance
- ///
- public HttpValueCollection()
- {
- }
-#else
+#if !NETFX_CORE // NameValueCollection is not serializable in netstandard1.3.
protected HttpValueCollection(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
+#endif
private HttpValueCollection()
: base(StringComparer.OrdinalIgnoreCase) // case-insensitive keys
{
}
-#endif
+
// Use a builder function instead of a ctor to avoid virtual calls from the ctor.
// The above condition is only important in the Full .NET fx implementation.
internal static HttpValueCollection Create()
@@ -75,9 +51,8 @@ internal static HttpValueCollection Create(IEnumerable
/// The name to be added as a case insensitive string.
/// The value to be added.
- public
-#if !NETFX_CORE
- override
-#endif
- void Add(string name, string value)
+ public override void Add(string name, string value)
{
ThrowIfMaxHttpCollectionKeysExceeded(Count);
name = name ?? String.Empty;
value = value ?? String.Empty;
-#if NETFX_CORE
- Names.Add(name);
- List.Add(new KeyValuePair(name, value));
-#else
base.Add(name, value);
-#endif
}
///
@@ -131,11 +97,7 @@ private string ToString(bool urlEncode)
StringBuilder builder = new StringBuilder();
bool first = true;
-#if NETFX_CORE
- foreach (string name in Names)
-#else
foreach (string name in this)
-#endif
{
string[] values = GetValues(name);
if (values == null || values.Length == 0)
@@ -179,104 +141,5 @@ private static bool AppendNameValuePair(StringBuilder builder, bool first, bool
}
return first;
}
-
-#if NETFX_CORE
- ///
- /// Gets the values associated with the specified name
- /// combined into one comma-separated list.
- ///
- /// The name of the entry that contains the values to get. The name can be null.
- /// A that contains a comma-separated list of url encoded values associated
- /// with the specified name if found; otherwise, null. The values are Url encoded.
- public string this[string name]
- {
- get
- {
- return Get(name);
- }
- }
-
- ///
- /// Gets the number of names in the collection.
- ///
- public int Count
- {
- get
- {
- return Names.Count;
- }
- }
-
- ///
- /// Gets the values associated with the specified name
- /// combined into one comma-separated list.
- ///
- /// The name of the entry that contains the values to get. The name can be null.
- ///
- /// A that contains a comma-separated list of url encoded values associated
- /// with the specified name if found; otherwise, null. The values are Url encoded.
- ///
- public string Get(string name)
- {
- name = name ?? String.Empty;
-
- if (!Names.Contains(name))
- {
- return null;
- }
-
- List values = GetValuesInternal(name);
- Contract.Assert(values != null && values.Count > 0);
-
- return String.Join(",", values);
- }
-
- ///
- /// Gets the values associated with the specified name.
- ///
- /// The
- /// A that contains url encoded values associated with the name, or null if the name does not exist.
- public string[] GetValues(string name)
- {
- name = name ?? String.Empty;
-
- if (!Names.Contains(name))
- {
- return null;
- }
-
- return GetValuesInternal(name).ToArray();
- }
-
- // call this when only when there are values available.
- private List GetValuesInternal(string name)
- {
- List values = new List();
-
- for (int i = 0; i < List.Count; i++)
- {
- KeyValuePair kvp = List[i];
-
- if (String.Equals(kvp.Key, name, StringComparison.OrdinalIgnoreCase))
- {
- values.Add(kvp.Value);
- }
- }
-
- return values;
- }
-
- ///
- public IEnumerator> GetEnumerator()
- {
- return List.GetEnumerator();
- }
-
- ///
- IEnumerator IEnumerable.GetEnumerator()
- {
- return List.GetEnumerator();
- }
-#endif
}
}
diff --git a/src/System.Net.Http.Formatting/UriExtensions.cs b/src/System.Net.Http.Formatting/UriExtensions.cs
index ab83ec938..cf531431a 100644
--- a/src/System.Net.Http.Formatting/UriExtensions.cs
+++ b/src/System.Net.Http.Formatting/UriExtensions.cs
@@ -18,21 +18,12 @@ namespace System.Net.Http
[EditorBrowsable(EditorBrowsableState.Never)]
public static class UriExtensions
{
-#if NETFX_CORE
- ///
- /// Parses the query portion of the specified .
- ///
- /// The instance from which to read.
- /// A containing the parsed result.
- public static HttpValueCollection ParseQueryString(this Uri address)
-#else
///
/// Parses the query portion of the specified .
///
/// The instance from which to read.
/// A containing the parsed result.
public static NameValueCollection ParseQueryString(this Uri address)
-#endif
{
if (address == null)
{
diff --git a/test/System.Net.Http.Formatting.Test/HttpContentFormDataExtensionsTest.cs b/test/System.Net.Http.Formatting.Test/HttpContentFormDataExtensionsTest.cs
index d82bc3687..cf787bf37 100644
--- a/test/System.Net.Http.Formatting.Test/HttpContentFormDataExtensionsTest.cs
+++ b/test/System.Net.Http.Formatting.Test/HttpContentFormDataExtensionsTest.cs
@@ -8,9 +8,6 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.TestCommon;
-#if NETFX_CORE
-using NameValueCollection = System.Net.Http.Formatting.HttpValueCollection;
-#endif
namespace System.Net.Http
{
diff --git a/test/System.Net.Http.Formatting.Test/Internal/HttpValueCollectionTest.cs b/test/System.Net.Http.Formatting.Test/Internal/HttpValueCollectionTest.cs
index 08ff25744..fdf8a58cb 100644
--- a/test/System.Net.Http.Formatting.Test/Internal/HttpValueCollectionTest.cs
+++ b/test/System.Net.Http.Formatting.Test/Internal/HttpValueCollectionTest.cs
@@ -4,9 +4,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Formatting;
-#if !NETFX_CORE
using System.Net.Http.Formatting.Internal;
-#endif
using System.Web.WebPages.TestUtils;
using Microsoft.TestCommon;
@@ -20,11 +18,7 @@ public class HttpValueCollectionTest
private static HttpValueCollection CreateInstance()
{
-#if NETFX_CORE
- return new HttpValueCollection();
-#else
return HttpValueCollection.Create();
-#endif
}
#if !NETCOREAPP
@@ -256,14 +250,8 @@ public void Create_InitializesCorrectly(IEnumerable
string expectedKey = kvp.Key ?? String.Empty;
string expectedValue = kvp.Value ?? String.Empty;
-#if NETFX_CORE
- KeyValuePair actualKvp = nvc.List[index];
- string actualKey = actualKvp.Key;
- string actualValue = actualKvp.Value;
-#else
string actualKey = nvc.AllKeys[index];
string actualValue = nvc[index];
-#endif
index++;
Assert.Equal(expectedKey, actualKey);
From d5ef276a5b8fc354a8a8ec4e81fa02ce62644b16 Mon Sep 17 00:00:00 2001
From: Doug Bunting <6431421+dougbu@users.noreply.github.com>
Date: Sun, 27 Nov 2022 17:51:21 -0800
Subject: [PATCH 08/19] Add `MaxDepth` support in NetCore assembly - remove an
unnecessary suppression about `MaxDepth` in `BaseJsonMediaTypeFormatter`
---
.../Formatting/BaseJsonMediaTypeFormatter.cs | 6 ------
.../Formatting/BsonMediaTypeFormatter.cs | 2 +-
.../Formatting/JsonMediaTypeFormatter.cs | 2 --
.../Formatting/XmlMediaTypeFormatter.cs | 4 ----
src/System.Net.Http.Formatting/FormattingUtilities.cs | 4 ----
.../Formatting/BsonMediaTypeFormatterTests.cs | 6 ------
.../Formatting/JsonMediaTypeFormatterTests.cs | 6 ------
.../Formatting/XmlMediaTypeFormatterTests.cs | 6 ------
.../Formatting/XmlSerializerMediaTypeFormatterTests.cs | 2 --
9 files changed, 1 insertion(+), 37 deletions(-)
diff --git a/src/System.Net.Http.Formatting/Formatting/BaseJsonMediaTypeFormatter.cs b/src/System.Net.Http.Formatting/Formatting/BaseJsonMediaTypeFormatter.cs
index b30a22edf..633881bed 100644
--- a/src/System.Net.Http.Formatting/Formatting/BaseJsonMediaTypeFormatter.cs
+++ b/src/System.Net.Http.Formatting/Formatting/BaseJsonMediaTypeFormatter.cs
@@ -50,19 +50,15 @@ protected BaseJsonMediaTypeFormatter()
/// Initializes a new instance of the class.
///
/// The instance to copy settings from.
-#if !NETFX_CORE
[SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors",
Justification = "MaxDepth is sealed in existing subclasses and its documentation carries warnings.")]
-#endif
protected BaseJsonMediaTypeFormatter(BaseJsonMediaTypeFormatter formatter)
: base(formatter)
{
Contract.Assert(formatter != null);
SerializerSettings = formatter.SerializerSettings;
-#if !NETFX_CORE // MaxDepth is not supported in portable library and so _maxDepth never changes there
MaxDepth = formatter._maxDepth;
-#endif
}
///
@@ -82,7 +78,6 @@ public JsonSerializerSettings SerializerSettings
}
}
-#if !NETFX_CORE // MaxDepth is not supported in portable library
///
/// Gets or sets the maximum depth allowed by this formatter.
///
@@ -106,7 +101,6 @@ public virtual int MaxDepth
_maxDepth = value;
}
}
-#endif
///
/// Creates a instance with the default settings used by the .
diff --git a/src/System.Net.Http.Formatting/Formatting/BsonMediaTypeFormatter.cs b/src/System.Net.Http.Formatting/Formatting/BsonMediaTypeFormatter.cs
index ca19cd4c6..4ae44e4d0 100644
--- a/src/System.Net.Http.Formatting/Formatting/BsonMediaTypeFormatter.cs
+++ b/src/System.Net.Http.Formatting/Formatting/BsonMediaTypeFormatter.cs
@@ -60,7 +60,6 @@ public static MediaTypeHeaderValue DefaultMediaType
}
}
-#if !NETFX_CORE // MaxDepth and DBNull not supported in portable library; no need to override there
///
public sealed override int MaxDepth
{
@@ -74,6 +73,7 @@ public sealed override int MaxDepth
}
}
+#if !NETFX_CORE // DBNull not supported in portable library; no need to override there
///
public override Task
public bool Indent { get; set; }
-#if !NETFX_CORE // MaxDepth not supported in portable library; no need to override there
///
public sealed override int MaxDepth
{
@@ -113,7 +112,6 @@ public sealed override int MaxDepth
_readerQuotas.MaxDepth = value;
}
}
-#endif
///
public override JsonReader CreateJsonReader(Type type, Stream readStream, Encoding effectiveEncoding)
diff --git a/src/System.Net.Http.Formatting/Formatting/XmlMediaTypeFormatter.cs b/src/System.Net.Http.Formatting/Formatting/XmlMediaTypeFormatter.cs
index c0f8a0336..2866741a7 100644
--- a/src/System.Net.Http.Formatting/Formatting/XmlMediaTypeFormatter.cs
+++ b/src/System.Net.Http.Formatting/Formatting/XmlMediaTypeFormatter.cs
@@ -55,9 +55,7 @@ protected XmlMediaTypeFormatter(XmlMediaTypeFormatter formatter)
{
UseXmlSerializer = formatter.UseXmlSerializer;
WriterSettings = formatter.WriterSettings;
-#if !NETFX_CORE // MaxDepth is not supported in portable libraries
MaxDepth = formatter.MaxDepth;
-#endif
}
///
@@ -106,7 +104,6 @@ public bool Indent
///
public XmlWriterSettings WriterSettings { get; private set; }
-#if !NETFX_CORE // MaxDepth is not supported in portable libraries
///
/// Gets or sets the maximum depth allowed by this formatter.
///
@@ -126,7 +123,6 @@ public int MaxDepth
_readerQuotas.MaxDepth = value;
}
}
-#endif
///
/// Registers the to use to read or write
diff --git a/src/System.Net.Http.Formatting/FormattingUtilities.cs b/src/System.Net.Http.Formatting/FormattingUtilities.cs
index a1ac95595..897bcd21f 100644
--- a/src/System.Net.Http.Formatting/FormattingUtilities.cs
+++ b/src/System.Net.Http.Formatting/FormattingUtilities.cs
@@ -166,9 +166,6 @@ public static HttpContentHeaders CreateEmptyContentHeaders()
///
public static XmlDictionaryReaderQuotas CreateDefaultReaderQuotas()
{
-#if NETFX_CORE // MaxDepth is a DOS mitigation. We don't support MaxDepth in portable libraries because it is strictly client side.
- return XmlDictionaryReaderQuotas.Max;
-#else
return new XmlDictionaryReaderQuotas()
{
MaxArrayLength = Int32.MaxValue,
@@ -177,7 +174,6 @@ public static XmlDictionaryReaderQuotas CreateDefaultReaderQuotas()
MaxNameTableCharCount = Int32.MaxValue,
MaxStringContentLength = Int32.MaxValue
};
-#endif
}
///
diff --git a/test/System.Net.Http.Formatting.Test/Formatting/BsonMediaTypeFormatterTests.cs b/test/System.Net.Http.Formatting.Test/Formatting/BsonMediaTypeFormatterTests.cs
index 96fde3311..3082b265d 100644
--- a/test/System.Net.Http.Formatting.Test/Formatting/BsonMediaTypeFormatterTests.cs
+++ b/test/System.Net.Http.Formatting.Test/Formatting/BsonMediaTypeFormatterTests.cs
@@ -149,9 +149,7 @@ void CopyConstructor()
// Arrange
TestBsonMediaTypeFormatter formatter = new TestBsonMediaTypeFormatter()
{
-#if !NETFX_CORE // MaxDepth is not supported in portable library
MaxDepth = 42,
-#endif
};
// Replace serializable settings and switch one property's value
@@ -163,16 +161,13 @@ void CopyConstructor()
TestBsonMediaTypeFormatter derivedFormatter = new TestBsonMediaTypeFormatter(formatter);
// Assert
-#if !NETFX_CORE // MaxDepth is not supported in portable library
Assert.Equal(formatter.MaxDepth, derivedFormatter.MaxDepth);
-#endif
Assert.NotSame(oldSettings, formatter.SerializerSettings);
Assert.NotEqual(oldSettings.CheckAdditionalContent, formatter.SerializerSettings.CheckAdditionalContent);
Assert.Same(formatter.SerializerSettings, derivedFormatter.SerializerSettings);
Assert.Same(formatter.SerializerSettings.ContractResolver, derivedFormatter.SerializerSettings.ContractResolver);
}
-#if !NETFX_CORE // MaxDepth is not supported in portable library
[Fact]
public void MaxDepth_RoundTrips()
{
@@ -187,7 +182,6 @@ public void MaxDepth_RoundTrips()
illegalUpperValue: null,
roundTripTestValue: 256);
}
-#endif
[Theory]
[TestDataSet(typeof(CommonUnitTestDataSets), "RepresentativeValueAndRefTypeTestDataCollection")]
diff --git a/test/System.Net.Http.Formatting.Test/Formatting/JsonMediaTypeFormatterTests.cs b/test/System.Net.Http.Formatting.Test/Formatting/JsonMediaTypeFormatterTests.cs
index 3d7898ddb..f2b878976 100644
--- a/test/System.Net.Http.Formatting.Test/Formatting/JsonMediaTypeFormatterTests.cs
+++ b/test/System.Net.Http.Formatting.Test/Formatting/JsonMediaTypeFormatterTests.cs
@@ -83,18 +83,14 @@ void CopyConstructor()
TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter()
{
Indent = true,
-#if !NETFX_CORE // MaxDepth and DCJS not supported in client portable library
MaxDepth = 42,
UseDataContractJsonSerializer = true
-#endif
};
TestJsonMediaTypeFormatter derivedFormatter = new TestJsonMediaTypeFormatter(formatter);
-#if !NETFX_CORE // MaxDepth and DCJS not supported in client portable library
Assert.Equal(formatter.MaxDepth, derivedFormatter.MaxDepth);
Assert.Equal(formatter.UseDataContractJsonSerializer, derivedFormatter.UseDataContractJsonSerializer);
-#endif
Assert.Equal(formatter.Indent, derivedFormatter.Indent);
Assert.Same(formatter.SerializerSettings, derivedFormatter.SerializerSettings);
Assert.Same(formatter.SerializerSettings.ContractResolver, derivedFormatter.SerializerSettings.ContractResolver);
@@ -117,7 +113,6 @@ public void Indent_RoundTrips()
expectedDefaultValue: false);
}
-#if !NETFX_CORE // MaxDepth is not supported in portable libraries
[Fact]
public void MaxDepth_RoundTrips()
{
@@ -131,7 +126,6 @@ public void MaxDepth_RoundTrips()
illegalUpperValue: null,
roundTripTestValue: 256);
}
-#endif
[Theory]
[TestDataSet(typeof(CommonUnitTestDataSets), "RepresentativeValueAndRefTypeTestDataCollection")]
diff --git a/test/System.Net.Http.Formatting.Test/Formatting/XmlMediaTypeFormatterTests.cs b/test/System.Net.Http.Formatting.Test/Formatting/XmlMediaTypeFormatterTests.cs
index 3d7fd1557..0a49a119d 100644
--- a/test/System.Net.Http.Formatting.Test/Formatting/XmlMediaTypeFormatterTests.cs
+++ b/test/System.Net.Http.Formatting.Test/Formatting/XmlMediaTypeFormatterTests.cs
@@ -67,18 +67,14 @@ void CopyConstructor()
TestXmlMediaTypeFormatter formatter = new TestXmlMediaTypeFormatter()
{
Indent = true,
-#if !NETFX_CORE // We don't support MaxDepth in the portable library
MaxDepth = 42,
-#endif
UseXmlSerializer = true
};
TestXmlMediaTypeFormatter derivedFormatter = new TestXmlMediaTypeFormatter(formatter);
Assert.Equal(formatter.Indent, derivedFormatter.Indent);
-#if !NETFX_CORE // We don't support MaxDepth in the portable library
Assert.Equal(formatter.MaxDepth, derivedFormatter.MaxDepth);
-#endif
Assert.Equal(formatter.UseXmlSerializer, derivedFormatter.UseXmlSerializer);
}
@@ -90,7 +86,6 @@ public void DefaultMediaType_ReturnsApplicationXml()
Assert.Equal("application/xml", mediaType.MediaType);
}
-#if !NETFX_CORE // We don't support MaxDepth in the portable library
[Fact]
public void MaxDepthReturnsCorrectValue()
{
@@ -115,7 +110,6 @@ public async Task ReadDeeplyNestedObjectThrows()
stream.Position = 0;
await Assert.ThrowsAsync(() => formatter.ReadFromStreamAsync(typeof(SampleType), stream, null, null));
}
-#endif
[Fact]
public void Indent_RoundTrips()
diff --git a/test/System.Net.Http.Formatting.Test/Formatting/XmlSerializerMediaTypeFormatterTests.cs b/test/System.Net.Http.Formatting.Test/Formatting/XmlSerializerMediaTypeFormatterTests.cs
index 0b48c73ff..033ca74fb 100644
--- a/test/System.Net.Http.Formatting.Test/Formatting/XmlSerializerMediaTypeFormatterTests.cs
+++ b/test/System.Net.Http.Formatting.Test/Formatting/XmlSerializerMediaTypeFormatterTests.cs
@@ -61,9 +61,7 @@ public async Task ReadDeeplyNestedObjectWorks()
{
XmlSerializerMediaTypeFormatter formatter = new XmlSerializerMediaTypeFormatter()
{
-#if !NETFX_CORE // We don't support MaxDepth in the portable library
MaxDepth = 5001
-#endif
};
StringContent content = new StringContent(GetDeeplyNestedObject(5000));
From 38f14277dca10418fea374218e90c0fdfa58300b Mon Sep 17 00:00:00 2001
From: Doug Bunting <6431421+dougbu@users.noreply.github.com>
Date: Sun, 27 Nov 2022 18:01:58 -0800
Subject: [PATCH 09/19] Add `UseDataContractJsonSerializer` support in NetCore
assembly - `XsdDataContractExporter` remains unavailable - update XML DCS
tests to handle this (previously not in NetCore assembly) - try various
workarounds - `ReadOnlyStreamWithEncodingPreamble` causes tests to hit
dotnet/runtime#80160 - restriction to UTF8 when writing is a significant
issue
---
.../Formatting/JsonMediaTypeFormatter.cs | 42 +++++++++++--------
.../Formatting/XmlMediaTypeFormatter.cs | 3 +-
...DataContractJsonMediaTypeFormatterTests.cs | 2 +
.../Formatting/JsonMediaTypeFormatterTests.cs | 10 -----
4 files changed, 29 insertions(+), 28 deletions(-)
diff --git a/src/System.Net.Http.Formatting/Formatting/JsonMediaTypeFormatter.cs b/src/System.Net.Http.Formatting/Formatting/JsonMediaTypeFormatter.cs
index 098ddc431..335d0c4d4 100644
--- a/src/System.Net.Http.Formatting/Formatting/JsonMediaTypeFormatter.cs
+++ b/src/System.Net.Http.Formatting/Formatting/JsonMediaTypeFormatter.cs
@@ -1,26 +1,18 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-#if !NETFX_CORE
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
-#endif
using System.Diagnostics.Contracts;
using System.IO;
using System.Net.Http.Headers;
-#if !NETFX_CORE
using System.Net.Http.Internal;
using System.Runtime.Serialization.Json;
-#endif
using System.Text;
using System.Threading;
-#if !NETFX_CORE
using System.Threading.Tasks;
-#endif
using System.Web.Http;
-#if !NETFX_CORE
using System.Xml;
-#endif
using Newtonsoft.Json;
namespace System.Net.Http.Formatting
@@ -30,11 +22,9 @@ namespace System.Net.Http.Formatting
///
public class JsonMediaTypeFormatter : BaseJsonMediaTypeFormatter
{
-#if !NETFX_CORE // DataContractJsonSerializer and MediaTypeMappings are not supported in portable library
private ConcurrentDictionary _dataContractSerializerCache = new ConcurrentDictionary();
private XmlDictionaryReaderQuotas _readerQuotas = FormattingUtilities.CreateDefaultReaderQuotas();
private RequestHeaderMapping _requestHeaderMapping;
-#endif
///
/// Initializes a new instance of the class.
@@ -60,10 +50,7 @@ protected JsonMediaTypeFormatter(JsonMediaTypeFormatter formatter)
{
Contract.Assert(formatter != null);
-#if !NETFX_CORE // UseDataContractJsonSerializer is not supported in portable library
UseDataContractJsonSerializer = formatter.UseDataContractJsonSerializer;
-#endif
-
Indent = formatter.Indent;
}
@@ -84,7 +71,6 @@ public static MediaTypeHeaderValue DefaultMediaType
get { return MediaTypeConstants.ApplicationJsonMediaType; }
}
-#if !NETFX_CORE // DataContractJsonSerializer is not supported in portable library
///
/// Gets or sets a value indicating whether to use by default.
///
@@ -92,7 +78,6 @@ public static MediaTypeHeaderValue DefaultMediaType
/// true if use by default; otherwise, false. The default is false.
///
public bool UseDataContractJsonSerializer { get; set; }
-#endif
///
/// Gets or sets a value indicating whether to indent elements when writing data.
@@ -161,7 +146,6 @@ public override JsonWriter CreateJsonWriter(Type type, Stream writeStream, Encod
return jsonWriter;
}
-#if !NETFX_CORE // DataContractJsonSerializer not supported in portable library; no need to override there
///
public override bool CanReadType(Type type)
{
@@ -231,10 +215,19 @@ public override object ReadFromStream(Type type, Stream readStream, Encoding eff
if (UseDataContractJsonSerializer)
{
DataContractJsonSerializer dataContractSerializer = GetDataContractSerializer(type);
+
+#if NETFX_CORE // JsonReaderWriterFactory is internal in netstandard1.3. Unfortunately, ignoring _readerQuotas.
+ // Force a preamble into the stream since DataContractJsonSerializer only supports auto-detecting
+ // encoding in netstandard1.3 ].
+ readStream = new ReadOnlyStreamWithEncodingPreamble(readStream, effectiveEncoding);
+
+ return dataContractSerializer.ReadObject(new NonClosingDelegatingStream(readStream));
+#else
using (XmlReader reader = JsonReaderWriterFactory.CreateJsonReader(new NonClosingDelegatingStream(readStream), effectiveEncoding, _readerQuotas, null))
{
return dataContractSerializer.ReadObject(reader);
}
+#endif
}
else
{
@@ -284,6 +277,14 @@ public override void WriteToStream(Type type, object value, Stream writeStream,
if (UseDataContractJsonSerializer)
{
+#if NETFX_CORE // DataContractJsonSerializer writes only UTF8 in netstandard1.3. Later versions of (now public)
+ // JsonReaderWriterFactory can compensate.
+ if (!string.Equals(Encoding.UTF8.WebName, effectiveEncoding.WebName, StringComparison.OrdinalIgnoreCase))
+ {
+ throw new NotSupportedException("!!! To be added !!!");
+ }
+#endif
+
if (MediaTypeFormatter.TryGetDelegatingTypeForIQueryableGenericOrSame(ref type))
{
if (value != null)
@@ -293,10 +294,15 @@ public override void WriteToStream(Type type, object value, Stream writeStream,
}
DataContractJsonSerializer dataContractSerializer = GetDataContractSerializer(type);
+
+#if NETFX_CORE // JsonReaderWriterFactory is internal in netstandard1.3.
+ dataContractSerializer.WriteObject(writeStream, value);
+#else
using (XmlWriter writer = JsonReaderWriterFactory.CreateJsonWriter(writeStream, effectiveEncoding, ownsStream: false))
{
dataContractSerializer.WriteObject(writer, value);
}
+#endif
}
else
{
@@ -314,8 +320,11 @@ private DataContractJsonSerializer CreateDataContractSerializer(Type type, bool
try
{
+#if !NETFX_CORE // XsdDataContractExporter is not supported in portable libraries
// Verify that type is a valid data contract by forcing the serializer to try to create a data contract
FormattingUtilities.XsdDataContractExporter.GetRootElementName(type);
+#endif
+
serializer = CreateDataContractSerializer(type);
}
catch (Exception caught)
@@ -376,6 +385,5 @@ private DataContractJsonSerializer GetDataContractSerializer(Type type)
return serializer;
}
-#endif
}
}
diff --git a/src/System.Net.Http.Formatting/Formatting/XmlMediaTypeFormatter.cs b/src/System.Net.Http.Formatting/Formatting/XmlMediaTypeFormatter.cs
index 2866741a7..560bb140d 100644
--- a/src/System.Net.Http.Formatting/Formatting/XmlMediaTypeFormatter.cs
+++ b/src/System.Net.Http.Formatting/Formatting/XmlMediaTypeFormatter.cs
@@ -509,11 +509,12 @@ private object CreateDefaultSerializer(Type type, bool throwOnError)
}
else
{
-#if !NETFX_CORE
+#if !NETFX_CORE // XsdDataContractExporter is not supported in portable libraries
// REVIEW: Is there something comparable in WinRT?
// Verify that type is a valid data contract by forcing the serializer to try to create a data contract
FormattingUtilities.XsdDataContractExporter.GetRootElementName(type);
#endif
+
serializer = CreateDataContractSerializer(type);
}
}
diff --git a/test/System.Net.Http.Formatting.Test/Formatting/DataContractJsonMediaTypeFormatterTests.cs b/test/System.Net.Http.Formatting.Test/Formatting/DataContractJsonMediaTypeFormatterTests.cs
index e576a6924..ba45f527a 100644
--- a/test/System.Net.Http.Formatting.Test/Formatting/DataContractJsonMediaTypeFormatterTests.cs
+++ b/test/System.Net.Http.Formatting.Test/Formatting/DataContractJsonMediaTypeFormatterTests.cs
@@ -87,6 +87,7 @@ public void CanReadType_ReturnsExpectedValues(Type variationType, object testDat
Assert.False(isSerializable != canSupport && isSerializable, String.Format("2nd CanReadType returned wrong value for '{0}'.", variationType));
}
+#if !NETFX_CORE // XsdDataContractExporterMethods unconditionally return true without XsdDataContractExporter to use.
[Fact]
public void CanReadType_ReturnsFalse_ForInvalidDataContracts()
{
@@ -100,6 +101,7 @@ public void CanWriteType_ReturnsFalse_ForInvalidDataContracts()
JsonMediaTypeFormatter formatter = new DataContractJsonMediaTypeFormatter();
Assert.False(formatter.CanWriteType(typeof(InvalidDataContract)));
}
+#endif
public class InvalidDataContract
{
diff --git a/test/System.Net.Http.Formatting.Test/Formatting/JsonMediaTypeFormatterTests.cs b/test/System.Net.Http.Formatting.Test/Formatting/JsonMediaTypeFormatterTests.cs
index f2b878976..76a497182 100644
--- a/test/System.Net.Http.Formatting.Test/Formatting/JsonMediaTypeFormatterTests.cs
+++ b/test/System.Net.Http.Formatting.Test/Formatting/JsonMediaTypeFormatterTests.cs
@@ -224,7 +224,6 @@ public async Task FormatterThrowsOnReadWhenOverridenCreateReturnsNull()
Assert.NotNull(formatter.InnerJsonSerializer);
}
-#if !NETFX_CORE
[Fact]
public async Task DataContractFormatterThrowsOnWriteWhenOverridenCreateFails()
{
@@ -309,7 +308,6 @@ public async Task DataContractFormatterThrowsOnReadWhenOverridenCreateReturnsNul
Assert.NotNull(formatter.InnerDataContractSerializer);
Assert.Null(formatter.InnerJsonSerializer);
}
-#endif
[Fact]
public void CanReadType_ReturnsTrueOnJtoken()
@@ -493,9 +491,7 @@ public async Task UseDataContractJsonSerializer_False()
{
JsonMediaTypeFormatter formatter = new JsonMediaTypeFormatter
{
-#if !NETFX_CORE // No JsonSerializer in portable libraries
UseDataContractJsonSerializer = false
-#endif
};
MemoryStream memoryStream = new MemoryStream();
HttpContent content = new StringContent(String.Empty);
@@ -512,9 +508,7 @@ public async Task UseDataContractJsonSerializer_False_Indent()
{
JsonMediaTypeFormatter formatter = new JsonMediaTypeFormatter
{
-#if !NETFX_CORE // No JsonSerializer in portable libraries
UseDataContractJsonSerializer = false,
-#endif
Indent = true
};
MemoryStream memoryStream = new MemoryStream();
@@ -532,9 +526,7 @@ public async Task UseJsonFormatterWithNull(Type type)
{
JsonMediaTypeFormatter formatter = new JsonMediaTypeFormatter
{
-#if !NETFX_CORE // No JsonSerializer in portable libraries
UseDataContractJsonSerializer = false
-#endif
};
MemoryStream memoryStream = new MemoryStream();
HttpContent content = new StringContent(String.Empty);
@@ -627,7 +619,6 @@ public override JsonSerializer CreateJsonSerializer()
return InnerJsonSerializer;
}
-#if !NETFX_CORE
public override DataContractJsonSerializer CreateDataContractSerializer(Type type)
{
InnerDataContractSerializer = base.CreateDataContractSerializer(type);
@@ -644,7 +635,6 @@ public override DataContractJsonSerializer CreateDataContractSerializer(Type typ
return InnerDataContractSerializer;
}
-#endif
}
private bool IsTypeSerializableWithJsonSerializer(Type type, object obj)
From a51922e0bb33a782969d285f3f16579137b1febc Mon Sep 17 00:00:00 2001
From: Doug Bunting <6431421+dougbu@users.noreply.github.com>
Date: Thu, 12 Jan 2023 22:37:12 -0800
Subject: [PATCH 10/19] Copy `TranscodingStream` and related files from
dotnet/runtime - my runtime clone was at
88868b7a781f4e5b9037b8721f30440207a7aa42
---
.../Internal/NullableAttributes.cs | 196 +++
.../Internal/TranscodingStream.cs | 612 ++++++++++
.../Internal/TranscodingStreamTests.cs | 1079 +++++++++++++++++
3 files changed, 1887 insertions(+)
create mode 100644 src/System.Net.Http.Formatting/Internal/NullableAttributes.cs
create mode 100644 src/System.Net.Http.Formatting/Internal/TranscodingStream.cs
create mode 100644 test/System.Net.Http.Formatting.Test/Internal/TranscodingStreamTests.cs
diff --git a/src/System.Net.Http.Formatting/Internal/NullableAttributes.cs b/src/System.Net.Http.Formatting/Internal/NullableAttributes.cs
new file mode 100644
index 000000000..361f88fa3
--- /dev/null
+++ b/src/System.Net.Http.Formatting/Internal/NullableAttributes.cs
@@ -0,0 +1,196 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Diagnostics.CodeAnalysis
+{
+#if !NETSTANDARD2_1
+ /// Specifies that null is allowed as an input even if the corresponding type disallows it.
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
+#if SYSTEM_PRIVATE_CORELIB
+ public
+#else
+ internal
+#endif
+ sealed class AllowNullAttribute : Attribute { }
+
+ /// Specifies that null is disallowed as an input even if the corresponding type allows it.
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
+#if SYSTEM_PRIVATE_CORELIB
+ public
+#else
+ internal
+#endif
+ sealed class DisallowNullAttribute : Attribute { }
+
+ /// Specifies that an output may be null even if the corresponding type disallows it.
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
+#if SYSTEM_PRIVATE_CORELIB
+ public
+#else
+ internal
+#endif
+ sealed class MaybeNullAttribute : Attribute { }
+
+ /// Specifies that an output will not be null even if the corresponding type allows it. Specifies that an input argument was not null when the call returns.
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
+#if SYSTEM_PRIVATE_CORELIB
+ public
+#else
+ internal
+#endif
+ sealed class NotNullAttribute : Attribute { }
+
+ /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it.
+ [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
+#if SYSTEM_PRIVATE_CORELIB
+ public
+#else
+ internal
+#endif
+ sealed class MaybeNullWhenAttribute : Attribute
+ {
+ /// Initializes the attribute with the specified return value condition.
+ ///
+ /// The return value condition. If the method returns this value, the associated parameter may be null.
+ ///
+ public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
+
+ /// Gets the return value condition.
+ public bool ReturnValue { get; }
+ }
+
+ /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it.
+ [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
+#if SYSTEM_PRIVATE_CORELIB
+ public
+#else
+ internal
+#endif
+ sealed class NotNullWhenAttribute : Attribute
+ {
+ /// Initializes the attribute with the specified return value condition.
+ ///
+ /// The return value condition. If the method returns this value, the associated parameter will not be null.
+ ///
+ public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
+
+ /// Gets the return value condition.
+ public bool ReturnValue { get; }
+ }
+
+ /// Specifies that the output will be non-null if the named parameter is non-null.
+ [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)]
+#if SYSTEM_PRIVATE_CORELIB
+ public
+#else
+ internal
+#endif
+ sealed class NotNullIfNotNullAttribute : Attribute
+ {
+ /// Initializes the attribute with the associated parameter name.
+ ///
+ /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null.
+ ///
+ public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName;
+
+ /// Gets the associated parameter name.
+ public string ParameterName { get; }
+ }
+
+ /// Applied to a method that will never return under any circumstance.
+ [AttributeUsage(AttributeTargets.Method, Inherited = false)]
+#if SYSTEM_PRIVATE_CORELIB
+ public
+#else
+ internal
+#endif
+ sealed class DoesNotReturnAttribute : Attribute { }
+
+ /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value.
+ [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
+#if SYSTEM_PRIVATE_CORELIB
+ public
+#else
+ internal
+#endif
+ sealed class DoesNotReturnIfAttribute : Attribute
+ {
+ /// Initializes the attribute with the specified parameter value.
+ ///
+ /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to
+ /// the associated parameter matches this value.
+ ///
+ public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue;
+
+ /// Gets the condition parameter value.
+ public bool ParameterValue { get; }
+ }
+#endif
+
+ /// Specifies that the method or property will ensure that the listed field and property members have not-null values.
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
+#if SYSTEM_PRIVATE_CORELIB
+ public
+#else
+ internal
+#endif
+ sealed class MemberNotNullAttribute : Attribute
+ {
+ /// Initializes the attribute with a field or property member.
+ ///
+ /// The field or property member that is promised to be not-null.
+ ///
+ public MemberNotNullAttribute(string member) => Members = new[] { member };
+
+ /// Initializes the attribute with the list of field and property members.
+ ///
+ /// The list of field and property members that are promised to be not-null.
+ ///
+ public MemberNotNullAttribute(params string[] members) => Members = members;
+
+ /// Gets field or property member names.
+ public string[] Members { get; }
+ }
+
+ /// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition.
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
+#if SYSTEM_PRIVATE_CORELIB
+ public
+#else
+ internal
+#endif
+ sealed class MemberNotNullWhenAttribute : Attribute
+ {
+ /// Initializes the attribute with the specified return value condition and a field or property member.
+ ///
+ /// The return value condition. If the method returns this value, the associated parameter will not be null.
+ ///
+ ///
+ /// The field or property member that is promised to be not-null.
+ ///
+ public MemberNotNullWhenAttribute(bool returnValue, string member)
+ {
+ ReturnValue = returnValue;
+ Members = new[] { member };
+ }
+
+ /// Initializes the attribute with the specified return value condition and list of field and property members.
+ ///
+ /// The return value condition. If the method returns this value, the associated parameter will not be null.
+ ///
+ ///
+ /// The list of field and property members that are promised to be not-null.
+ ///
+ public MemberNotNullWhenAttribute(bool returnValue, params string[] members)
+ {
+ ReturnValue = returnValue;
+ Members = members;
+ }
+
+ /// Gets the return value condition.
+ public bool ReturnValue { get; }
+
+ /// Gets field or property member names.
+ public string[] Members { get; }
+ }
+}
diff --git a/src/System.Net.Http.Formatting/Internal/TranscodingStream.cs b/src/System.Net.Http.Formatting/Internal/TranscodingStream.cs
new file mode 100644
index 000000000..65be587e1
--- /dev/null
+++ b/src/System.Net.Http.Formatting/Internal/TranscodingStream.cs
@@ -0,0 +1,612 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Buffers;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace System.Text
+{
+ internal sealed class TranscodingStream : Stream
+ {
+ private const int DefaultReadByteBufferSize = 4 * 1024; // lifted from StreamReader.cs (FileStream)
+
+ // We optimistically assume 1 byte ~ 1 char during transcoding. This is a good rule of thumb
+ // but isn't always appropriate: transcoding between single-byte and multi-byte encodings
+ // will violate this, as will any invalid data fixups performed by the transcoder itself.
+ // To account for these unknowns we have a minimum scratch buffer size we use during the
+ // transcoding process. This should be generous enough to account for even the largest
+ // fallback mechanism we're likely to see in the real world.
+
+ private const int MinWriteRentedArraySize = 4 * 1024;
+ private const int MaxWriteRentedArraySize = 1024 * 1024;
+
+ private readonly Encoding _innerEncoding;
+ private readonly Encoding _thisEncoding;
+ private Stream _innerStream; // null if the wrapper has been disposed
+ private readonly bool _leaveOpen;
+
+ /*
+ * Fields used for writing bytes [this] -> chars -> bytes [inner]
+ * Lazily initialized the first time we need to write
+ */
+
+ private Encoder? _innerEncoder;
+ private Decoder? _thisDecoder;
+
+ /*
+ * Fields used for reading bytes [inner] -> chars -> bytes [this]
+ * Lazily initialized the first time we need to read
+ */
+
+ private Encoder? _thisEncoder;
+ private Decoder? _innerDecoder;
+ private int _readCharBufferMaxSize; // the maximum number of characters _innerDecoder.ReadChars can return
+ private byte[]? _readBuffer; // contains the data that Read() should return
+ private int _readBufferOffset;
+ private int _readBufferCount;
+
+ internal TranscodingStream(Stream innerStream, Encoding innerEncoding, Encoding thisEncoding, bool leaveOpen)
+ {
+ Debug.Assert(innerStream != null);
+ Debug.Assert(innerEncoding != null);
+ Debug.Assert(thisEncoding != null);
+
+ _innerStream = innerStream;
+ _leaveOpen = leaveOpen;
+
+ _innerEncoding = innerEncoding;
+ _thisEncoding = thisEncoding;
+ }
+
+ /*
+ * Most CanXyz methods delegate to the inner stream, returning false
+ * if this instance has been disposed. CanSeek is always false.
+ */
+
+ public override bool CanRead => _innerStream?.CanRead ?? false;
+
+ public override bool CanSeek => false;
+
+ public override bool CanWrite => _innerStream?.CanWrite ?? false;
+
+ public override long Length => throw new NotSupportedException(SR.NotSupported_UnseekableStream);
+
+ public override long Position
+ {
+ get => throw new NotSupportedException(SR.NotSupported_UnseekableStream);
+ set => ThrowHelper.ThrowNotSupportedException_UnseekableStream();
+ }
+
+ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
+ => TaskToApm.Begin(ReadAsync(buffer, offset, count, CancellationToken.None), callback, state);
+
+ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
+ => TaskToApm.Begin(WriteAsync(buffer, offset, count, CancellationToken.None), callback, state);
+
+ protected override void Dispose(bool disposing)
+ {
+ Debug.Assert(disposing, "This type isn't finalizable.");
+
+ if (_innerStream is null)
+ {
+ return; // dispose called multiple times, ignore
+ }
+
+ // First, flush any pending data to the inner stream.
+
+ ArraySegment pendingData = FinalFlushWriteBuffers();
+ if (pendingData.Count != 0)
+ {
+ _innerStream.Write(pendingData);
+ }
+
+ // Mark our object as disposed
+
+ Stream innerStream = _innerStream;
+ _innerStream = null!;
+
+ // And dispose the inner stream if needed
+
+ if (!_leaveOpen)
+ {
+ innerStream.Dispose();
+ }
+ }
+
+ public override ValueTask DisposeAsync()
+ {
+ if (_innerStream is null)
+ {
+ return default; // dispose called multiple times, ignore
+ }
+
+ // First, get any pending data destined for the inner stream.
+
+ ArraySegment pendingData = FinalFlushWriteBuffers();
+
+ if (pendingData.Count == 0)
+ {
+ // Fast path: just dispose of the object graph.
+ // No need to write anything to the stream first.
+
+ Stream innerStream = _innerStream;
+ _innerStream = null!;
+
+ return (_leaveOpen)
+ ? default /* no work to do */
+ : innerStream.DisposeAsync();
+ }
+
+ // Slower path; need to perform an async write followed by an async dispose.
+
+ return DisposeAsyncCore(pendingData);
+ async ValueTask DisposeAsyncCore(ArraySegment pendingData)
+ {
+ Debug.Assert(pendingData.Count != 0);
+
+ Stream innerStream = _innerStream;
+ _innerStream = null!;
+
+ await innerStream.WriteAsync(pendingData.AsMemory()).ConfigureAwait(false);
+
+ if (!_leaveOpen)
+ {
+ await innerStream.DisposeAsync().ConfigureAwait(false);
+ }
+ }
+ }
+
+ public override int EndRead(IAsyncResult asyncResult)
+ => TaskToApm.End(asyncResult);
+
+ public override void EndWrite(IAsyncResult asyncResult)
+ => TaskToApm.End(asyncResult);
+
+#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant
+#pragma warning disable CS8774 // Member must have a non-null value when exiting.
+
+ // Sets up the data structures that are necessary before any read operation takes place,
+ // throwing if the object is in a state where reads are not possible.
+ [MemberNotNull(nameof(_innerDecoder), nameof(_thisEncoder), nameof(_readBuffer))]
+ private void EnsurePreReadConditions()
+ {
+ ThrowIfDisposed();
+ if (_innerDecoder is null)
+ {
+ InitializeReadDataStructures();
+ }
+
+ void InitializeReadDataStructures()
+ {
+ if (!CanRead)
+ {
+ ThrowHelper.ThrowNotSupportedException_UnreadableStream();
+ }
+
+ _innerDecoder = _innerEncoding.GetDecoder();
+ _thisEncoder = _thisEncoding.GetEncoder();
+ _readCharBufferMaxSize = _innerEncoding.GetMaxCharCount(DefaultReadByteBufferSize);
+
+ // Can't use ArrayPool for the below array since it's an instance field of this object.
+ // But since we never expose the raw array contents to our callers we can get away
+ // with skipping the array zero-init during allocation. The segment points to the
+ // data which we haven't yet read; however, we own the entire backing array and can
+ // re-create the segment as needed once the array is repopulated.
+
+ _readBuffer = GC.AllocateUninitializedArray(_thisEncoding.GetMaxByteCount(_readCharBufferMaxSize));
+ }
+ }
+
+ // Sets up the data structures that are necessary before any write operation takes place,
+ // throwing if the object is in a state where writes are not possible.
+ [MemberNotNull(nameof(_thisDecoder), nameof(_innerEncoder))]
+ private void EnsurePreWriteConditions()
+ {
+ ThrowIfDisposed();
+ if (_innerEncoder is null)
+ {
+ InitializeReadDataStructures();
+ }
+
+ void InitializeReadDataStructures()
+ {
+ if (!CanWrite)
+ {
+ ThrowHelper.ThrowNotSupportedException_UnwritableStream();
+ }
+
+ _innerEncoder = _innerEncoding.GetEncoder();
+ _thisDecoder = _thisEncoding.GetDecoder();
+ }
+ }
+
+#pragma warning restore CS8774 // Member must have a non-null value when exiting.
+#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant
+
+ // returns any pending data that needs to be flushed to the inner stream before disposal
+ private ArraySegment FinalFlushWriteBuffers()
+ {
+ // If this stream was never used for writing, no-op.
+
+ if (_thisDecoder is null || _innerEncoder is null)
+ {
+ return default;
+ }
+
+ // convert bytes [this] -> chars
+ // Having leftover data in our buffers should be very rare since it should only
+ // occur if the end of the stream contains an incomplete multi-byte sequence.
+ // Let's not bother complicating this logic with array pool rentals or allocation-
+ // avoiding loops.
+
+
+ char[] chars = Array.Empty();
+ int charCount = _thisDecoder.GetCharCount(Array.Empty(), 0, 0, flush: true);
+ if (charCount > 0)
+ {
+ chars = new char[charCount];
+ charCount = _thisDecoder.GetChars(Array.Empty(), 0, 0, chars, 0, flush: true);
+ }
+
+ // convert chars -> bytes [inner]
+ // It's possible that _innerEncoder might need to perform some end-of-text fixup
+ // (due to flush: true), even if _thisDecoder didn't need to do so.
+
+ byte[] bytes = Array.Empty();
+ int byteCount = _innerEncoder.GetByteCount(chars, 0, charCount, flush: true);
+ if (byteCount > 0)
+ {
+ bytes = new byte[byteCount];
+ byteCount = _innerEncoder.GetBytes(chars, 0, charCount, bytes, 0, flush: true);
+ }
+
+ return new ArraySegment(bytes, 0, byteCount);
+ }
+
+ public override void Flush()
+ {
+ // Don't pass flush: true to our inner decoder + encoder here, since it could cause data
+ // corruption if a flush occurs mid-stream. Wait until the stream is being closed.
+
+ ThrowIfDisposed();
+ _innerStream.Flush();
+ }
+
+ public override Task FlushAsync(CancellationToken cancellationToken)
+ {
+ // Don't pass flush: true to our inner decoder + encoder here, since it could cause data
+ // corruption if a flush occurs mid-stream. Wait until the stream is being closed.
+
+ ThrowIfDisposed();
+ return _innerStream.FlushAsync(cancellationToken);
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ ValidateBufferArguments(buffer, offset, count);
+
+ return Read(new Span(buffer, offset, count));
+ }
+
+ public override int Read(Span buffer)
+ {
+ EnsurePreReadConditions();
+
+ // If there's no data in our pending read buffer, we'll need to populate it from
+ // the inner stream. We read the inner stream's bytes, decode that to chars using
+ // the 'inner' encoding, then re-encode those chars under the 'this' encoding.
+ // We've already calculated the worst-case expansions for the intermediate buffers,
+ // so we use GetChars / GetBytes instead of Convert to simplify the below code
+ // and to ensure an exception is thrown if the Encoding reported an incorrect
+ // worst-case expansion.
+
+ if (_readBufferCount == 0)
+ {
+ byte[] rentedBytes = ArrayPool.Shared.Rent(DefaultReadByteBufferSize);
+ char[] rentedChars = ArrayPool.Shared.Rent(_readCharBufferMaxSize);
+
+ try
+ {
+ int pendingReadDataPopulatedJustNow;
+ bool isEofReached;
+
+ do
+ {
+ // Beware: Use our constant value instead of 'rentedBytes.Length' for the count
+ // parameter below. The reason for this is that the array pool could've returned
+ // a larger-than-expected array, but our worst-case expansion calculations
+ // performed earlier didn't take that into account.
+
+ int innerBytesReadJustNow = _innerStream.Read(rentedBytes, 0, DefaultReadByteBufferSize);
+ isEofReached = (innerBytesReadJustNow == 0);
+
+ // Convert bytes [inner] -> chars, then convert chars -> bytes [this].
+ // We can't return 0 to our caller until inner stream EOF has been reached. But if the
+ // inner stream returns a non-empty but incomplete buffer, GetBytes may return 0 anyway
+ // since it can't yet make forward progress on the input data. If this happens, we'll
+ // loop so that we don't return 0 to our caller until we truly see inner stream EOF.
+
+ int charsDecodedJustNow = _innerDecoder.GetChars(rentedBytes, 0, innerBytesReadJustNow, rentedChars, 0, flush: isEofReached);
+ pendingReadDataPopulatedJustNow = _thisEncoder.GetBytes(rentedChars, 0, charsDecodedJustNow, _readBuffer, 0, flush: isEofReached);
+ } while (!isEofReached && pendingReadDataPopulatedJustNow == 0);
+
+ _readBufferOffset = 0;
+ _readBufferCount = pendingReadDataPopulatedJustNow;
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(rentedBytes);
+ ArrayPool.Shared.Return(rentedChars);
+ }
+ }
+
+ // At this point: (a) we've populated our pending read buffer and there's
+ // useful data to return to our caller; or (b) the pending read buffer is
+ // empty because the inner stream has reached EOF and all pending read data
+ // has already been flushed, and we should return 0.
+
+ int bytesToReturn = Math.Min(_readBufferCount, buffer.Length);
+ _readBuffer.AsSpan(_readBufferOffset, bytesToReturn).CopyTo(buffer);
+ _readBufferOffset += bytesToReturn;
+ _readBufferCount -= bytesToReturn;
+ return bytesToReturn;
+ }
+
+ public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ ValidateBufferArguments(buffer, offset, count);
+
+ return ReadAsync(new Memory(buffer, offset, count), cancellationToken).AsTask();
+ }
+
+ public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken)
+ {
+ EnsurePreReadConditions();
+
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return ValueTask.FromCanceled(cancellationToken);
+ }
+
+ return ReadAsyncCore(buffer, cancellationToken);
+ async ValueTask ReadAsyncCore(Memory buffer, CancellationToken cancellationToken)
+ {
+ // If there's no data in our pending read buffer, we'll need to populate it from
+ // the inner stream. We read the inner stream's bytes, decode that to chars using
+ // the 'inner' encoding, then re-encode those chars under the 'this' encoding.
+ // We've already calculated the worst-case expansions for the intermediate buffers,
+ // so we use GetChars / GetBytes instead of Convert to simplify the below code
+ // and to ensure an exception is thrown if the Encoding reported an incorrect
+ // worst-case expansion.
+
+ if (_readBufferCount == 0)
+ {
+ byte[] rentedBytes = ArrayPool.Shared.Rent(DefaultReadByteBufferSize);
+ char[] rentedChars = ArrayPool.Shared.Rent(_readCharBufferMaxSize);
+
+ try
+ {
+ int pendingReadDataPopulatedJustNow;
+ bool isEofReached;
+
+ do
+ {
+ // Beware: Use our constant value instead of 'rentedBytes.Length' when creating
+ // the Mem struct. The reason for this is that the array pool could've returned
+ // a larger-than-expected array, but our worst-case expansion calculations
+ // performed earlier didn't take that into account.
+
+ int innerBytesReadJustNow = await _innerStream.ReadAsync(rentedBytes.AsMemory(0, DefaultReadByteBufferSize), cancellationToken).ConfigureAwait(false);
+ isEofReached = (innerBytesReadJustNow == 0);
+
+ // Convert bytes [inner] -> chars, then convert chars -> bytes [this].
+ // We can't return 0 to our caller until inner stream EOF has been reached. But if the
+ // inner stream returns a non-empty but incomplete buffer, GetBytes may return 0 anyway
+ // since it can't yet make forward progress on the input data. If this happens, we'll
+ // loop so that we don't return 0 to our caller until we truly see inner stream EOF.
+
+ int charsDecodedJustNow = _innerDecoder.GetChars(rentedBytes, 0, innerBytesReadJustNow, rentedChars, 0, flush: isEofReached);
+ pendingReadDataPopulatedJustNow = _thisEncoder.GetBytes(rentedChars, 0, charsDecodedJustNow, _readBuffer, 0, flush: isEofReached);
+ } while (!isEofReached && pendingReadDataPopulatedJustNow == 0);
+
+ _readBufferOffset = 0;
+ _readBufferCount = pendingReadDataPopulatedJustNow;
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(rentedBytes);
+ ArrayPool.Shared.Return(rentedChars);
+ }
+ }
+
+ // At this point: (a) we've populated our pending read buffer and there's
+ // useful data to return to our caller; or (b) the pending read buffer is
+ // empty because the inner stream has reached EOF and all pending read data
+ // has already been flushed, and we should return 0.
+
+ int bytesToReturn = Math.Min(_readBufferCount, buffer.Length);
+ _readBuffer.AsSpan(_readBufferOffset, bytesToReturn).CopyTo(buffer.Span);
+ _readBufferOffset += bytesToReturn;
+ _readBufferCount -= bytesToReturn;
+ return bytesToReturn;
+ }
+ }
+
+ public override unsafe int ReadByte()
+ {
+ byte b = 0;
+ return Read(new Span(ref b)) != 0 ? b : -1;
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ => throw new NotSupportedException(SR.NotSupported_UnseekableStream);
+
+ public override void SetLength(long value)
+ => ThrowHelper.ThrowNotSupportedException_UnseekableStream();
+
+ [StackTraceHidden]
+ private void ThrowIfDisposed()
+ {
+ if (_innerStream is null)
+ {
+ ThrowObjectDisposedException();
+ }
+ }
+
+ [DoesNotReturn]
+ [StackTraceHidden]
+ private void ThrowObjectDisposedException()
+ {
+ ThrowHelper.ThrowObjectDisposedException_StreamClosed(GetType().Name);
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ ValidateBufferArguments(buffer, offset, count);
+
+ Write(new ReadOnlySpan(buffer, offset, count));
+ }
+
+ public override void Write(ReadOnlySpan buffer)
+ {
+ EnsurePreWriteConditions();
+
+ if (buffer.IsEmpty)
+ {
+ return;
+ }
+
+ int rentalLength = Math.Clamp(buffer.Length, MinWriteRentedArraySize, MaxWriteRentedArraySize);
+
+ char[] scratchChars = ArrayPool.Shared.Rent(rentalLength);
+ byte[] scratchBytes = ArrayPool.Shared.Rent(rentalLength);
+
+ try
+ {
+ bool decoderFinished, encoderFinished;
+ do
+ {
+ // convert bytes [this] -> chars
+
+ _thisDecoder.Convert(
+ bytes: buffer,
+ chars: scratchChars,
+ flush: false,
+ out int bytesConsumed,
+ out int charsWritten,
+ out decoderFinished);
+
+ buffer = buffer.Slice(bytesConsumed);
+
+ // convert chars -> bytes [inner]
+
+ Span decodedChars = scratchChars.AsSpan(0, charsWritten);
+
+ do
+ {
+ _innerEncoder.Convert(
+ chars: decodedChars,
+ bytes: scratchBytes,
+ flush: false,
+ out int charsConsumed,
+ out int bytesWritten,
+ out encoderFinished);
+
+ decodedChars = decodedChars.Slice(charsConsumed);
+
+ // It's more likely that the inner stream provides an optimized implementation of
+ // Write(byte[], ...) over Write(ROS), so we'll prefer the byte[]-based overloads.
+
+ _innerStream.Write(scratchBytes, 0, bytesWritten);
+ } while (!encoderFinished);
+ } while (!decoderFinished);
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(scratchChars);
+ ArrayPool.Shared.Return(scratchBytes);
+ }
+ }
+
+ public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ ValidateBufferArguments(buffer, offset, count);
+
+ return WriteAsync(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask();
+ }
+
+ public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken)
+ {
+ EnsurePreWriteConditions();
+
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return ValueTask.FromCanceled(cancellationToken);
+ }
+
+ if (buffer.IsEmpty)
+ {
+ return ValueTask.CompletedTask;
+ }
+
+ return WriteAsyncCore(buffer, cancellationToken);
+ async ValueTask WriteAsyncCore(ReadOnlyMemory remainingOuterEncodedBytes, CancellationToken cancellationToken)
+ {
+ int rentalLength = Math.Clamp(remainingOuterEncodedBytes.Length, MinWriteRentedArraySize, MaxWriteRentedArraySize);
+
+ char[] scratchChars = ArrayPool.Shared.Rent(rentalLength);
+ byte[] scratchBytes = ArrayPool.Shared.Rent(rentalLength);
+
+ try
+ {
+ bool decoderFinished, encoderFinished;
+ do
+ {
+ // convert bytes [this] -> chars
+
+ _thisDecoder.Convert(
+ bytes: remainingOuterEncodedBytes.Span,
+ chars: scratchChars,
+ flush: false,
+ out int bytesConsumed,
+ out int charsWritten,
+ out decoderFinished);
+
+ remainingOuterEncodedBytes = remainingOuterEncodedBytes.Slice(bytesConsumed);
+
+ // convert chars -> bytes [inner]
+
+ ArraySegment decodedChars = new ArraySegment(scratchChars, 0, charsWritten);
+
+ do
+ {
+ _innerEncoder.Convert(
+ chars: decodedChars,
+ bytes: scratchBytes,
+ flush: false,
+ out int charsConsumed,
+ out int bytesWritten,
+ out encoderFinished);
+
+ decodedChars = decodedChars.Slice(charsConsumed);
+ await _innerStream.WriteAsync(new ReadOnlyMemory(scratchBytes, 0, bytesWritten), cancellationToken).ConfigureAwait(false);
+ } while (!encoderFinished);
+ } while (!decoderFinished);
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(scratchChars);
+ ArrayPool.Shared.Return(scratchBytes);
+ }
+ }
+ }
+
+ public override void WriteByte(byte value)
+ => Write(new ReadOnlySpan(in value));
+ }
+}
diff --git a/test/System.Net.Http.Formatting.Test/Internal/TranscodingStreamTests.cs b/test/System.Net.Http.Formatting.Test/Internal/TranscodingStreamTests.cs
new file mode 100644
index 000000000..6f1dbd29b
--- /dev/null
+++ b/test/System.Net.Http.Formatting.Test/Internal/TranscodingStreamTests.cs
@@ -0,0 +1,1079 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.IO.Pipelines;
+using System.IO.Tests;
+using System.Threading;
+using System.Threading.Tasks;
+using Moq;
+using Xunit;
+
+namespace System.Text.Tests
+{
+ public class TranscodingStreamTests : ConnectedStreamConformanceTests
+ {
+ protected override bool FlushRequiredToWriteData => true;
+ protected override bool FlushGuaranteesAllDataWritten => false;
+ protected override bool BlocksOnZeroByteReads => true;
+
+ protected override Task CreateConnectedStreamsAsync()
+ {
+ (Stream stream1, Stream stream2) = ConnectedStreams.CreateBidirectional();
+ return Task.FromResult(
+ (Encoding.CreateTranscodingStream(stream1, new IdentityEncoding(), new IdentityEncoding()),
+ Encoding.CreateTranscodingStream(stream2, new IdentityEncoding(), new IdentityEncoding())));
+ }
+
+ public static IEnumerable