diff --git a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionEnumerator.cs b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionEnumerator.cs
index 49139052..6f940aee 100644
--- a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionEnumerator.cs
+++ b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionEnumerator.cs
@@ -5,14 +5,20 @@
namespace Asp.Versioning.Http;
+#if NET
+using System.Buffers;
+#endif
using System.Collections;
+#if NET
+using static System.StringSplitOptions;
+#endif
///
/// Represents an enumerator of API versions from a HTTP header.
///
public readonly struct ApiVersionEnumerator : IEnumerable
{
- private readonly IEnumerable values;
+ private readonly string[] values;
private readonly IApiVersionParser parser;
///
@@ -29,37 +35,72 @@ public ApiVersionEnumerator(
ArgumentNullException.ThrowIfNull( response );
ArgumentException.ThrowIfNullOrEmpty( headerName );
- this.values =
- response.Headers.TryGetValues( headerName, out var values )
- ? values
- : Enumerable.Empty();
-
+ this.values = response.Headers.TryGetValues( headerName, out var values ) ? values.ToArray() : [];
this.parser = parser ?? ApiVersionParser.Default;
}
///
public IEnumerator GetEnumerator()
{
- using var iterator = values.GetEnumerator();
+#if NETSTANDARD
+ for ( var i = 0; i < values.Length; i++ )
+ {
+ var items = values[i].Split( ',' );
- if ( !iterator.MoveNext() )
+ for ( var j = 0; j < items.Length; j++ )
+ {
+ var item = items[j].Trim();
+
+ if ( item.Length > 0 && parser.TryParse( item, out var result ) )
+ {
+ yield return result!;
+ }
+ }
+ }
+#else
+ for ( var i = 0; i < values.Length; i++ )
{
- yield break;
+ var (count, versions) = ParseVersions( values[i] );
+
+ for ( var j = 0; j < count; j++ )
+ {
+ yield return versions[j];
+ }
}
+#endif
+ }
- if ( parser.TryParse( iterator.Current, out var value ) )
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+#if NET
+ private (int Count, ApiVersion[] Results) ParseVersions( ReadOnlySpan value )
+ {
+ var pool = ArrayPool.Shared;
+ var ranges = pool.Rent( 5 );
+ var length = value.Split( ranges, ',', RemoveEmptyEntries | TrimEntries );
+
+ while ( length >= ranges.Length )
{
- yield return value!;
+ pool.Return( ranges );
+ length <<= 1;
+ ranges = pool.Rent( length );
+ length = value.Split( ranges, ',', RemoveEmptyEntries | TrimEntries );
}
- while ( iterator.MoveNext() )
+ var results = new ApiVersion[length];
+ var count = 0;
+
+ for ( var i = 0; i < length; i++ )
{
- if ( parser.TryParse( iterator.Current, out value ) )
+ var text = value[ranges[i]];
+
+ if ( text.Length > 0 && parser.TryParse( text, out var result ) )
{
- yield return value!;
+ results[count++] = result;
}
}
- }
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+ pool.Return( ranges );
+ return (count, results);
+ }
+#endif
}
\ No newline at end of file
diff --git a/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj b/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj
index 75416005..e508c993 100644
--- a/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj
+++ b/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj
@@ -1,7 +1,7 @@
- 8.0.0
+ 8.0.1
8.0.0.0
$(DefaultTargetFramework);netstandard1.1;netstandard2.0
Asp.Versioning.Http
diff --git a/src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt b/src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt
index 5f282702..0cf60623 100644
--- a/src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt
+++ b/src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt
@@ -1 +1 @@
-
\ No newline at end of file
+Parse single header CSVs ([#1070](https://github.com/dotnet/aspnet-api-versioning/issues/1070))
\ No newline at end of file
diff --git a/src/Client/test/Asp.Versioning.Http.Client.Tests/ApiVersionEnumeratorTest.cs b/src/Client/test/Asp.Versioning.Http.Client.Tests/ApiVersionEnumeratorTest.cs
new file mode 100644
index 00000000..864a8d92
--- /dev/null
+++ b/src/Client/test/Asp.Versioning.Http.Client.Tests/ApiVersionEnumeratorTest.cs
@@ -0,0 +1,116 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning.Http;
+
+public class ApiVersionEnumeratorTest
+{
+ [Fact]
+ public void enumerator_should_process_single_header_value()
+ {
+ // arrange
+ var response = new HttpResponseMessage();
+
+ response.Headers.Add( "api-supported-versions", "1.0" );
+
+ var enumerator = new ApiVersionEnumerator( response, "api-supported-versions" );
+
+ // act
+ var results = enumerator.ToArray();
+
+ // assert
+ results.Should().BeEquivalentTo( [new ApiVersion( 1.0 )] );
+ }
+
+ [Fact]
+ public void enumerator_should_process_multiple_header_values()
+ {
+ // arrange
+ var response = new HttpResponseMessage();
+
+ response.Headers.Add( "api-supported-versions", ["1.0", "2.0"] );
+
+ var enumerator = new ApiVersionEnumerator( response, "api-supported-versions" );
+
+ // act
+ var results = enumerator.ToArray();
+
+ // assert
+ results.Should().BeEquivalentTo( new ApiVersion[] { new( 1.0 ), new( 2.0 ) } );
+ }
+
+ [Theory]
+ [InlineData( "1.0,2.0" )]
+ [InlineData( "1.0, 2.0" )]
+ [InlineData( "1.0,,2.0" )]
+ [InlineData( "1.0, abc, 2.0" )]
+ public void enumerator_should_process_single_header_comma_separated_values( string value )
+ {
+ // arrange
+ var response = new HttpResponseMessage();
+
+ response.Headers.Add( "api-supported-versions", [value] );
+
+ var enumerator = new ApiVersionEnumerator( response, "api-supported-versions" );
+
+ // act
+ var results = enumerator.ToArray();
+
+ // assert
+ results.Should().BeEquivalentTo( new ApiVersion[] { new( 1.0 ), new( 2.0 ) } );
+ }
+
+ [Fact]
+ public void enumerator_should_process_many_header_comma_separated_values()
+ {
+ // arrange
+ const string Value = "1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0";
+ var response = new HttpResponseMessage();
+
+ response.Headers.Add( "api-supported-versions", [Value] );
+
+ var enumerator = new ApiVersionEnumerator( response, "api-supported-versions" );
+
+ // act
+ var results = enumerator.ToArray();
+
+ // assert
+ results.Should().BeEquivalentTo(
+ new ApiVersion[]
+ {
+ new( 1.0 ),
+ new( 2.0 ),
+ new( 3.0 ),
+ new( 4.0 ),
+ new( 5.0 ),
+ new( 6.0 ),
+ new( 7.0 ),
+ new( 8.0 ),
+ new( 9.0 ),
+ new( 10.0 ),
+ } );
+ }
+
+ [Fact]
+ public void enumerator_should_process_multiple_header_comma_separated_values()
+ {
+ // arrange
+ var response = new HttpResponseMessage();
+
+ response.Headers.Add( "api-supported-versions", ["1.0, 2.0", "3.0, 4.0"] );
+
+ var enumerator = new ApiVersionEnumerator( response, "api-supported-versions" );
+
+ // act
+ var results = enumerator.ToArray();
+
+ // assert
+ results.Should().BeEquivalentTo(
+ new ApiVersion[]
+ {
+ new( 1.0 ),
+ new( 2.0 ),
+ new( 3.0 ),
+ new( 4.0 ),
+ } );
+ }
+}
\ No newline at end of file