Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 8ac43c5

Browse filesBrowse files
Parse versions single header CSVs. Fixes #1070
1 parent 59ff9e6 commit 8ac43c5
Copy full SHA for 8ac43c5

File tree

Expand file treeCollapse file tree

2 files changed

+173
-16
lines changed
Filter options
Expand file treeCollapse file tree

2 files changed

+173
-16
lines changed

‎src/Client/src/Asp.Versioning.Http.Client/ApiVersionEnumerator.cs

Copy file name to clipboardExpand all lines: src/Client/src/Asp.Versioning.Http.Client/ApiVersionEnumerator.cs
+57-16Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,20 @@
55

66
namespace Asp.Versioning.Http;
77

8+
#if NET
9+
using System.Buffers;
10+
#endif
811
using System.Collections;
12+
#if NET
13+
using static System.StringSplitOptions;
14+
#endif
915

1016
/// <summary>
1117
/// Represents an enumerator of API versions from a HTTP header.
1218
/// </summary>
1319
public readonly struct ApiVersionEnumerator : IEnumerable<ApiVersion>
1420
{
15-
private readonly IEnumerable<string> values;
21+
private readonly string[] values;
1622
private readonly IApiVersionParser parser;
1723

1824
/// <summary>
@@ -29,37 +35,72 @@ public ApiVersionEnumerator(
2935
ArgumentNullException.ThrowIfNull( response );
3036
ArgumentException.ThrowIfNullOrEmpty( headerName );
3137

32-
this.values =
33-
response.Headers.TryGetValues( headerName, out var values )
34-
? values
35-
: Enumerable.Empty<string>();
36-
38+
this.values = response.Headers.TryGetValues( headerName, out var values ) ? values.ToArray() : [];
3739
this.parser = parser ?? ApiVersionParser.Default;
3840
}
3941

4042
/// <inheritdoc />
4143
public IEnumerator<ApiVersion> GetEnumerator()
4244
{
43-
using var iterator = values.GetEnumerator();
45+
#if NETSTANDARD
46+
for ( var i = 0; i < values.Length; i++ )
47+
{
48+
var items = values[i].Split( ',' );
4449

45-
if ( !iterator.MoveNext() )
50+
for ( var j = 0; j < items.Length; j++ )
51+
{
52+
var item = items[j].Trim();
53+
54+
if ( item.Length > 0 && parser.TryParse( item, out var result ) )
55+
{
56+
yield return result!;
57+
}
58+
}
59+
}
60+
#else
61+
for ( var i = 0; i < values.Length; i++ )
4662
{
47-
yield break;
63+
var (count, versions) = ParseVersions( values[i] );
64+
65+
for ( var j = 0; j < count; j++ )
66+
{
67+
yield return versions[j];
68+
}
4869
}
70+
#endif
71+
}
4972

50-
if ( parser.TryParse( iterator.Current, out var value ) )
73+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
74+
#if NET
75+
private (int Count, ApiVersion[] Results) ParseVersions( ReadOnlySpan<char> value )
76+
{
77+
var pool = ArrayPool<Range>.Shared;
78+
var ranges = pool.Rent( 5 );
79+
var length = value.Split( ranges, ',', RemoveEmptyEntries | TrimEntries );
80+
81+
while ( length >= ranges.Length )
5182
{
52-
yield return value!;
83+
pool.Return( ranges );
84+
length <<= 1;
85+
ranges = pool.Rent( length );
86+
length = value.Split( ranges, ',', RemoveEmptyEntries | TrimEntries );
5387
}
5488

55-
while ( iterator.MoveNext() )
89+
var results = new ApiVersion[length];
90+
var count = 0;
91+
92+
for ( var i = 0; i < length; i++ )
5693
{
57-
if ( parser.TryParse( iterator.Current, out value ) )
94+
var text = value[ranges[i]];
95+
96+
if ( text.Length > 0 && parser.TryParse( text, out var result ) )
5897
{
59-
yield return value!;
98+
results[count++] = result;
6099
}
61100
}
62-
}
63101

64-
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
102+
pool.Return( ranges );
103+
return (count, results);
104+
}
105+
#endif
65106
}
+116Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// Copyright (c) .NET Foundation and contributors. All rights reserved.
2+
3+
namespace Asp.Versioning.Http;
4+
5+
public class ApiVersionEnumeratorTest
6+
{
7+
[Fact]
8+
public void enumerator_should_process_single_header_value()
9+
{
10+
// arrange
11+
var response = new HttpResponseMessage();
12+
13+
response.Headers.Add( "api-supported-versions", "1.0" );
14+
15+
var enumerator = new ApiVersionEnumerator( response, "api-supported-versions" );
16+
17+
// act
18+
var results = enumerator.ToArray();
19+
20+
// assert
21+
results.Should().BeEquivalentTo( [new ApiVersion( 1.0 )] );
22+
}
23+
24+
[Fact]
25+
public void enumerator_should_process_multiple_header_values()
26+
{
27+
// arrange
28+
var response = new HttpResponseMessage();
29+
30+
response.Headers.Add( "api-supported-versions", ["1.0", "2.0"] );
31+
32+
var enumerator = new ApiVersionEnumerator( response, "api-supported-versions" );
33+
34+
// act
35+
var results = enumerator.ToArray();
36+
37+
// assert
38+
results.Should().BeEquivalentTo( new ApiVersion[] { new( 1.0 ), new( 2.0 ) } );
39+
}
40+
41+
[Theory]
42+
[InlineData( "1.0,2.0" )]
43+
[InlineData( "1.0, 2.0" )]
44+
[InlineData( "1.0,,2.0" )]
45+
[InlineData( "1.0, abc, 2.0" )]
46+
public void enumerator_should_process_single_header_comma_separated_values( string value )
47+
{
48+
// arrange
49+
var response = new HttpResponseMessage();
50+
51+
response.Headers.Add( "api-supported-versions", [value] );
52+
53+
var enumerator = new ApiVersionEnumerator( response, "api-supported-versions" );
54+
55+
// act
56+
var results = enumerator.ToArray();
57+
58+
// assert
59+
results.Should().BeEquivalentTo( new ApiVersion[] { new( 1.0 ), new( 2.0 ) } );
60+
}
61+
62+
[Fact]
63+
public void enumerator_should_process_many_header_comma_separated_values()
64+
{
65+
// arrange
66+
const string Value = "1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0";
67+
var response = new HttpResponseMessage();
68+
69+
response.Headers.Add( "api-supported-versions", [Value] );
70+
71+
var enumerator = new ApiVersionEnumerator( response, "api-supported-versions" );
72+
73+
// act
74+
var results = enumerator.ToArray();
75+
76+
// assert
77+
results.Should().BeEquivalentTo(
78+
new ApiVersion[]
79+
{
80+
new( 1.0 ),
81+
new( 2.0 ),
82+
new( 3.0 ),
83+
new( 4.0 ),
84+
new( 5.0 ),
85+
new( 6.0 ),
86+
new( 7.0 ),
87+
new( 8.0 ),
88+
new( 9.0 ),
89+
new( 10.0 ),
90+
} );
91+
}
92+
93+
[Fact]
94+
public void enumerator_should_process_multiple_header_comma_separated_values()
95+
{
96+
// arrange
97+
var response = new HttpResponseMessage();
98+
99+
response.Headers.Add( "api-supported-versions", ["1.0, 2.0", "3.0, 4.0"] );
100+
101+
var enumerator = new ApiVersionEnumerator( response, "api-supported-versions" );
102+
103+
// act
104+
var results = enumerator.ToArray();
105+
106+
// assert
107+
results.Should().BeEquivalentTo(
108+
new ApiVersion[]
109+
{
110+
new( 1.0 ),
111+
new( 2.0 ),
112+
new( 3.0 ),
113+
new( 4.0 ),
114+
} );
115+
}
116+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.