xamarin / xamarin-macios

.NET for iOS, Mac Catalyst, macOS, and tvOS provide open-source bindings of the Apple SDKs for use with .NET managed languages such as C#
Other
2.49k stars 515 forks source link

OData.Client throws ArgumentException #17331

Open Mat3oo opened 1 year ago

Mat3oo commented 1 year ago

Description

When using OData.Client nuget package (https://www.nuget.org/packages/Microsoft.OData.Client/) on MacCatalyst or iOS with MAUI, application throws System.ArgumentException while executing query on OData context. Other platforms (Android, Windows) behave as expected - return queried data. Exception message tells that the key has been already added. In my case I was able to spot two keys, causing problem:

I tested this with local API and I can tell that HTTP call reaches WebAPI endpoint, so I guess the problem is in response processing.

Exception message: "An item with the same key has already been added. Key: X-AspNet-Version"

Steps to Reproduce

Using provided example repo:

  1. Run testOdataMaui project on MacCatalyst or iOS.
  2. Click "Click Me" button.
  3. Exception will be displayed via Alert modal.
  4. Mac Catalyst project also avaliable in odataMacCatalystExample project

Expected Behavior

OData client executes query without exception.

Actual Behavior

OData Client throws exception on iOS and Mac Catalyst platforms.

Exception message: "An item with the same key has already been added. Key: X-AspNet-Version"

Exception StackTrace:
at System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.String, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].TryInsert(String key, String value, InsertionBehavior behavior)
   at System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.String, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].Add(String key, String value)
   at System.Linq.Enumerable.ToDictionary[KeyValuePair`2,String,String](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
   at System.Linq.Enumerable.ToDictionary[KeyValuePair`2,String,String](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector)
   at Microsoft.OData.Client.HttpClientRequestMessage.ConvertHttpWebResponse(HttpResponseMessage response)
   at Microsoft.OData.Client.HttpClientRequestMessage.<GetResponse>b__42_0()
   at Microsoft.OData.Client.HttpClientRequestMessage.UnwrapAggregateException[HttpWebResponseMessage](Func`1 action)
   at Microsoft.OData.Client.HttpClientRequestMessage.GetResponse()
   at Microsoft.OData.Client.ODataRequestMessageWrapper.GetResponse()
   at Microsoft.OData.Client.DataServiceContext.GetResponseHelper(ODataRequestMessageWrapper request, IAsyncResult asyncResult, Boolean handleWebException)
   at Microsoft.OData.Client.DataServiceContext.GetSynchronousResponse(ODataRequestMessageWrapper request, Boolean handleWebException)
   at Microsoft.OData.Client.RequestInfo.GetSynchronousResponse(ODataRequestMessageWrapper request, Boolean handleWebException)
   at Microsoft.OData.Client.QueryResult.ExecuteQuery()
   at Microsoft.OData.Client.DataServiceRequest.Execute[PeopleModel](DataServiceContext context, QueryComponents queryComponents)
   at Microsoft.OData.Client.DataServiceQuery`1[[testOdataMaui.PeopleModel, testOdataMaui, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].Execute()
   at testOdataMaui.MainPage.OnCounterClicked(Object sender, EventArgs e) in /Users/mat3o/Repos/testOdataMaui/MainPage.xaml.cs:line 18

Environment

Version information ``` Visual Studio Community 2022 for Mac Version 17.4.2 (build 17) Installation UUID: c9028d46-6a98-4597-961a-40555b3452f1 Runtime .NET 6.0.12 (64-bit) Architecture: Arm64 Roslyn (Language Service) 6.4.0-6.22578.12+3c6ab8e1715e5b080fb7bb77070810ab71e09387 NuGet Version: 6.3.1.1 .NET SDK (Arm64) SDK: /usr/local/share/dotnet/sdk/7.0.102/Sdks SDK Versions: 7.0.102 7.0.404 MSBuild SDKs: /Applications/Visual Studio.app/Contents/MonoBundle/MSBuild/Current/bin/Sdks .NET SDK (x64) SDK Version: 3.1.426 .NET Runtime (Arm64) Runtime: /usr/local/share/dotnet/dotnet Runtime Versions: 8.0.2 6.0.12 .NET Runtime (x64) Runtime: /usr/local/share/dotnet/x64/dotnet Runtime Version: 3.1.32 Xamarin.Profiler Version: 1.8.0.19 Location: /Applications/Xamarin Profiler.app/Contents/MacOS/Xamarin Profiler Updater Version: 11 Xamarin.Android Version: 13.1.0.1 (Visual Studio Community) Commit: xamarin-android/d17-4/13ba222 Android SDK: /Users/user1/Library/Developer/Xamarin/android-sdk-macosx Supported Android versions: 13.0 (API level 33) SDK Command-line Tools Version: 7.0 SDK Platform Tools Version: 33.0.2 SDK Build Tools Version: 32.0.0 Build Information: Mono: a96bde9 Java.Interop: xamarin/java.interop/d17-4@fcc33ce2 SQLite: xamarin/sqlite/3.39.3@23e1ae7 Xamarin.Android Tools: xamarin/xamarin-android-tools/main@0be567a Microsoft Build of OpenJDK Java SDK: /Library/Java/JavaVirtualMachines/microsoft-11.jdk 11.0.12 Android Designer EPL code available here: https://github.com/xamarin/AndroidDesigner.EPL Eclipse Temurin JDK Java SDK: /Library/Java/JavaVirtualMachines/temurin-8.jdk 1.8.0.302 Android Designer EPL code available here: https://github.com/xamarin/AndroidDesigner.EPL Android SDK Manager Version: 17.4.0.54 Hash: 6eabb9e Branch: remotes/origin/d17-4 Build date: 2022-12-13 00:02:50 UTC Android Device Manager Version: 0.0.0.1206 Hash: 886af39 Branch: 886af39 Build date: 2022-12-13 00:02:50 UTC Xamarin Designer Version: 17.4.0.136 Hash: d49c9ff6d3 Branch: remotes/origin/d17-4 Build date: 2022-12-13 00:02:45 UTC Apple Developer Tools Xcode 14.2 (21534) Build 14C18 Xamarin.Mac Xamarin.Mac not installed. Can't find /Library/Frameworks/Xamarin.Mac.framework/Versions/Current/Version. Xamarin.iOS Version: 16.1.1.27 (Visual Studio Community) Hash: 933c6c2c9 Branch: xcode14.1 Build date: 2022-11-22 02:00:37-0500 Build Information Release ID: 1704020017 Git revision: 7ef5413f2eec3351ce648085f619fc29fd8cb647 Build date: 2022-12-13 00:00:15+00 Build branch: release-17.4 Build lane: release-17.4 Operating System Mac OS X 13.1.0 Darwin 22.2.0 Darwin Kernel Version 22.2.0 Fri Nov 11 02:06:26 PST 2022 root:xnu-8792.61.2~4/RELEASE_ARM64_T8112 arm64 ```

Build Logs

Example Project (If Possible)

https://github.com/Mat3oo/Maui_OdataClient_example

rolfbjarne commented 1 year ago

Thanks for the sample, I can reproduce this.

Some debugging shows this:

response.Headers:

Screen Shot 2023-01-24 at 08 55 40

response.Content.Headers:

Screen Shot 2023-01-24 at 08 55 48

The symptom is that there are duplicated headers between response.Headers and response.Content.Headers, and then this code triggers the exception:

https://github.com/OData/odata.net/blob/61e8f6664fa10e84ac55fd48bcf2a49333bda6a6/src/Microsoft.OData.Client/Serialization/HttpClientRequestMessage.cs#L471-L481

I'm not certain exactly what's the difference between these two collections, but it seems response.Content.Headers should only be the "Content-*" headers, and clearly response.Content.Headers contain other headers according to the screenshot.

If that's the case, I believe the fix is to add header filtering around here:

https://github.com/xamarin/xamarin-macios/blob/8f6eb025423bcdaf77b04be60885e92269e07148/src/Foundation/NSUrlSessionHandler.cs#L876

For reference, this is the response if I use curl: https://gist.github.com/rolfbjarne/4670e780f31200f186222b6a2b5e9247

CC @mandel-macaque

rolfbjarne commented 1 year ago

17491 is a likely duplicate.

rolfbjarne commented 1 year ago

This is how CFNetworkHandler does it:

https://github.com/xamarin/xamarin-macios/blob/ab61de421f41fc30825d4b008f83431d7e1ca743/src/System.Net.Http/CFNetworkHandler.cs#L352-L355

ecomation-matthijs commented 3 months ago

I run into the same issue, while this always worked for me until the release of the 8.0.0 release. Is there a solution for this or a workaround?

image
rolfbjarne commented 3 months ago

@ecomation-matthijs could you get a complete stack trace? It's not clear from that stack trace whether it's the same issue or not.

ecomation-matthijs commented 3 months ago

@rolfbjarne Heres the stacktrace:

System.ArgumentException: An item with the same key has already been added. Key: OData-Version
   at System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].TryInsert(String key, String value, InsertionBehavior behavior)
   at System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].Add(String key, String value)
   at System.Linq.Enumerable.ToDictionary[KeyValuePair`2,String,String](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
   at System.Linq.Enumerable.ToDictionary[KeyValuePair`2,String,String](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector)
   at Microsoft.OData.Client.HttpClientRequestMessage.ConvertHttpWebResponse(HttpResponseMessage response)
   at Microsoft.OData.Client.HttpClientRequestMessage.<GetResponse>b__38_0()
   at Microsoft.OData.Client.HttpClientRequestMessage.UnwrapAggregateException[HttpWebResponseMessage](Func`1 action)
   at Microsoft.OData.Client.HttpClientRequestMessage.GetResponse()
   at Microsoft.OData.Client.ODataRequestMessageWrapper.GetResponse()
   at Microsoft.OData.Client.DataServiceContext.GetResponseHelper(ODataRequestMessageWrapper request, IAsyncResult asyncResult, Boolean handleWebException)
   at Microsoft.OData.Client.DataServiceContext.GetSynchronousResponse(ODataRequestMessageWrapper request, Boolean handleWebException)
   at Microsoft.OData.Client.RequestInfo.GetSynchronousResponse(ODataRequestMessageWrapper request, Boolean handleWebException)
   at Microsoft.OData.Client.QueryResult.ExecuteQuery()
   at Microsoft.OData.Client.DataServiceRequest.GetValue[Int32](DataServiceContext context, Func`2 parseQueryResultFunc)
   at Microsoft.OData.Client.DataServiceQueryProvider.ReturnSingleton[Int32](Expression expression)
   at Microsoft.OData.Client.DataServiceQueryProvider.Execute[Int32](Expression expression)
   at System.Linq.Queryable.Count[PerformanceModel](IQueryable`1 source)
   at AppMobile.Modules.Time.Pages.TimeOverview.TimeOverviewViewModel.RefreshItemsAsync() in C:\repo\app-mobile\Modules\Time\Pages\TimeOverview\TimeOverviewViewModel.cs:line 241
rolfbjarne commented 2 months ago
System.ArgumentException: An item with the same key has already been added. Key: OData-Version
   at System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].TryInsert(String key, String value, InsertionBehavior behavior)
   at System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].Add(String key, String value)
   at System.Linq.Enumerable.ToDictionary[KeyValuePair`2,String,String](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
   at System.Linq.Enumerable.ToDictionary[KeyValuePair`2,String,String](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector)
   at Microsoft.OData.Client.HttpClientRequestMessage.ConvertHttpWebResponse(HttpResponseMessage response)
   at Microsoft.OData.Client.HttpClientRequestMessage.<GetResponse>b__38_0()
   at Microsoft.OData.Client.HttpClientRequestMessage.UnwrapAggregateException[HttpWebResponseMessage](Func`1 action)
   at Microsoft.OData.Client.HttpClientRequestMessage.GetResponse()
   at Microsoft.OData.Client.ODataRequestMessageWrapper.GetResponse()
   at Microsoft.OData.Client.DataServiceContext.GetResponseHelper(ODataRequestMessageWrapper request, IAsyncResult asyncResult, Boolean handleWebException)
   at Microsoft.OData.Client.DataServiceContext.GetSynchronousResponse(ODataRequestMessageWrapper request, Boolean handleWebException)
   at Microsoft.OData.Client.RequestInfo.GetSynchronousResponse(ODataRequestMessageWrapper request, Boolean handleWebException)
   at Microsoft.OData.Client.QueryResult.ExecuteQuery()
   at Microsoft.OData.Client.DataServiceRequest.GetValue[Int32](DataServiceContext context, Func`2 parseQueryResultFunc)
   at Microsoft.OData.Client.DataServiceQueryProvider.ReturnSingleton[Int32](Expression expression)
   at Microsoft.OData.Client.DataServiceQueryProvider.Execute[Int32](Expression expression)
   at System.Linq.Queryable.Count[PerformanceModel](IQueryable`1 source)
   at AppMobile.Modules.Time.Pages.TimeOverview.TimeOverviewViewModel.RefreshItemsAsync() in C:\repo\app-mobile\Modules\Time\Pages\TimeOverview\TimeOverviewViewModel.cs:line 241

Yes, that looks very similar, so it's probably the same problem.