Skip to content

Commit 5aaff20

Browse files
Add encoding tests
1 parent 2ad2da1 commit 5aaff20

File tree

4 files changed

+79
-5
lines changed

4 files changed

+79
-5
lines changed

src/ImageSharp.Web/Caching/UriAbsoluteCacheKey.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ public class UriAbsoluteCacheKey : ICacheKey
1313
{
1414
/// <inheritdoc/>
1515
public string Create(HttpContext context, CommandCollection commands)
16-
=> CaseHandlingUriBuilder.BuildAbsolute(CaseHandlingUriBuilder.CaseHandling.None, context.Request.Host, context.Request.PathBase, context.Request.Path, QueryString.Create(commands));
16+
=> CaseHandlingUriBuilder.BuildAbsolute(
17+
CaseHandlingUriBuilder.CaseHandling.None,
18+
context.Request.Host,
19+
context.Request.PathBase,
20+
context.Request.Path,
21+
QueryString.Create(commands));
1722
}
1823
}

src/ImageSharp.Web/Caching/UriAbsoluteLowerInvariantCacheKey.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ public class UriAbsoluteLowerInvariantCacheKey : ICacheKey
1313
{
1414
/// <inheritdoc/>
1515
public string Create(HttpContext context, CommandCollection commands)
16-
=> CaseHandlingUriBuilder.BuildAbsolute(CaseHandlingUriBuilder.CaseHandling.LowerInvariant, context.Request.Host, context.Request.PathBase, context.Request.Path, QueryString.Create(commands));
16+
=> CaseHandlingUriBuilder.BuildAbsolute(
17+
CaseHandlingUriBuilder.CaseHandling.LowerInvariant,
18+
context.Request.Host,
19+
context.Request.PathBase,
20+
context.Request.Path,
21+
QueryString.Create(commands));
1722
}
1823
}

src/ImageSharp.Web/CaseHandlingUriBuilder.cs

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Web
1515
public static class CaseHandlingUriBuilder
1616
{
1717
private static readonly Uri FallbackBaseUri = new("http://localhost/");
18-
private static readonly SpanAction<char, (bool LowerInvariant, string Host, string PathBase, string Path, string Query)> InitializeAbsoluteUriStringSpanAction = new(InitializeAbsoluteUriString);
18+
private static readonly SpanAction<char, (bool LowerInvariant, string Scheme, string Host, string PathBase, string Path, string Query)> InitializeAbsoluteUriStringSpanAction = new(InitializeAbsoluteUriString);
1919

2020
/// <summary>
2121
/// Provides Uri case handling options.
@@ -50,30 +50,54 @@ public static string BuildRelative(
5050
// Take any potential performance hit vs concatination for code reading sanity.
5151
=> BuildAbsolute(handling, default, pathBase, path, query);
5252

53+
/// <summary>
54+
/// Combines the given URI components into a string that is properly encoded for use in HTTP headers.
55+
/// Note that unicode in the HostString will be encoded as punycode and the scheme is not included
56+
/// in the result.
57+
/// </summary>
58+
/// <param name="handling">Determines case handling for the result. <paramref name="query"/> is always converted to invariant lowercase.</param>
59+
/// <param name="host">The host portion of the uri normally included in the Host header. This may include the port.</param>
60+
/// <param name="pathBase">The first portion of the request path associated with application root.</param>
61+
/// <param name="path">The portion of the request path that identifies the requested resource.</param>
62+
/// <param name="query">The query, if any.</param>
63+
/// <returns>The combined URI components, properly encoded for use in HTTP headers.</returns>
64+
public static string BuildAbsolute(
65+
CaseHandling handling,
66+
HostString host,
67+
PathString pathBase = default,
68+
PathString path = default,
69+
QueryString query = default)
70+
=> BuildAbsolute(handling, string.Empty, host, pathBase, path, query);
71+
5372
/// <summary>
5473
/// Combines the given URI components into a string that is properly encoded for use in HTTP headers.
5574
/// Note that unicode in the HostString will be encoded as punycode.
5675
/// </summary>
5776
/// <param name="handling">Determines case handling for the result. <paramref name="query"/> is always converted to invariant lowercase.</param>
77+
/// <param name="scheme">http, https, etc.</param>
5878
/// <param name="host">The host portion of the uri normally included in the Host header. This may include the port.</param>
5979
/// <param name="pathBase">The first portion of the request path associated with application root.</param>
6080
/// <param name="path">The portion of the request path that identifies the requested resource.</param>
6181
/// <param name="query">The query, if any.</param>
6282
/// <returns>The combined URI components, properly encoded for use in HTTP headers.</returns>
6383
public static string BuildAbsolute(
6484
CaseHandling handling,
85+
string scheme,
6586
HostString host,
6687
PathString pathBase = default,
6788
PathString path = default,
6889
QueryString query = default)
6990
{
91+
Guard.NotNull(scheme, nameof(scheme));
92+
7093
string hostText = host.ToUriComponent();
7194
string pathBaseText = pathBase.ToUriComponent();
7295
string pathText = path.ToUriComponent();
7396
string queryText = query.ToUriComponent();
7497

7598
// PERF: Calculate string length to allocate correct buffer size for string.Create.
7699
int length =
100+
(scheme.Length > 0 ? scheme.Length + Uri.SchemeDelimiter.Length : 0) +
77101
hostText.Length +
78102
pathBaseText.Length +
79103
pathText.Length +
@@ -95,7 +119,10 @@ public static string BuildAbsolute(
95119
length--;
96120
}
97121

98-
return string.Create(length, (handling == CaseHandling.LowerInvariant, hostText, pathBaseText, pathText, queryText), InitializeAbsoluteUriStringSpanAction);
122+
return string.Create(
123+
length,
124+
(handling == CaseHandling.LowerInvariant, scheme, hostText, pathBaseText, pathText, queryText),
125+
InitializeAbsoluteUriStringSpanAction);
99126
}
100127

101128
/// <summary>
@@ -122,6 +149,7 @@ public static string Encode(CaseHandling handling, Uri uri)
122149
{
123150
return BuildAbsolute(
124151
handling,
152+
scheme: uri.Scheme,
125153
host: HostString.FromUriComponent(uri),
126154
pathBase: PathString.FromUriComponent(uri),
127155
query: QueryString.FromUriComponent(uri));
@@ -167,7 +195,7 @@ private static int CopyTextToBufferLowerInvariant(Span<char> buffer, int index,
167195
/// </summary>
168196
/// <param name="buffer">The URI <see cref="string"/>'s <see cref="char"/> buffer.</param>
169197
/// <param name="uriParts">The URI parts.</param>
170-
private static void InitializeAbsoluteUriString(Span<char> buffer, (bool Lower, string Host, string PathBase, string Path, string Query) uriParts)
198+
private static void InitializeAbsoluteUriString(Span<char> buffer, (bool Lower, string Scheme, string Host, string PathBase, string Path, string Query) uriParts)
171199
{
172200
int index = 0;
173201
ReadOnlySpan<char> pathBaseSpan = uriParts.PathBase.AsSpan();
@@ -180,6 +208,12 @@ private static void InitializeAbsoluteUriString(Span<char> buffer, (bool Lower,
180208
pathBaseSpan = pathBaseSpan.Slice(0, pathBaseSpan.Length - 1);
181209
}
182210

211+
if (uriParts.Scheme.Length > 0)
212+
{
213+
index = CopyTextToBuffer(buffer, index, uriParts.Scheme.AsSpan());
214+
index = CopyTextToBuffer(buffer, index, Uri.SchemeDelimiter.AsSpan());
215+
}
216+
183217
if (uriParts.Lower)
184218
{
185219
index = CopyTextToBufferLowerInvariant(buffer, index, uriParts.Host.AsSpan());
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using Xunit;
5+
6+
namespace SixLabors.ImageSharp.Web.Tests.Helpers
7+
{
8+
public class CaseHandlingUriBuilderTests
9+
{
10+
[Theory]
11+
[InlineData(CaseHandlingUriBuilder.CaseHandling.None, "https://sixlabors.com:443/Path/?query=1")]
12+
[InlineData(CaseHandlingUriBuilder.CaseHandling.LowerInvariant, "https://sixlabors.com:443/path/?query=1")]
13+
public void CanEncodeAbsoluteUri(CaseHandlingUriBuilder.CaseHandling value, string expected)
14+
{
15+
const string input = "https://sixlabors.com/Path/?Query=1";
16+
string actual = CaseHandlingUriBuilder.Encode(value, input);
17+
Assert.Equal(expected, actual);
18+
}
19+
20+
[Theory]
21+
[InlineData(CaseHandlingUriBuilder.CaseHandling.None, "/Path/?query=1")]
22+
[InlineData(CaseHandlingUriBuilder.CaseHandling.LowerInvariant, "/path/?query=1")]
23+
public void CanEncodeRelativeUri(CaseHandlingUriBuilder.CaseHandling value, string expected)
24+
{
25+
const string input = "/Path/?Query=1";
26+
string actual = CaseHandlingUriBuilder.Encode(value, input);
27+
Assert.Equal(expected, actual);
28+
}
29+
}
30+
}

0 commit comments

Comments
 (0)