@@ -14,7 +14,8 @@ namespace SixLabors.ImageSharp.Web
1414 /// </summary>
1515 public static class CaseHandlingUriBuilder
1616 {
17- private static readonly SpanAction < char , ( bool LowerInvariant , string Host , string PathBase , string Path , string Query ) > InitializeAbsoluteUriStringSpanAction = new ( InitializeAbsoluteUriString ) ;
17+ private static readonly Uri FallbackBaseUri = new ( "http://localhost/" ) ;
18+ private static readonly SpanAction < char , ( bool LowerInvariant , string Scheme , string Host , string PathBase , string Path , string Query ) > InitializeAbsoluteUriStringSpanAction = new ( InitializeAbsoluteUriString ) ;
1819
1920 /// <summary>
2021 /// Provides Uri case handling options.
@@ -49,30 +50,54 @@ public static string BuildRelative(
4950 // Take any potential performance hit vs concatination for code reading sanity.
5051 => BuildAbsolute ( handling , default , pathBase , path , query ) ;
5152
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+
5272 /// <summary>
5373 /// Combines the given URI components into a string that is properly encoded for use in HTTP headers.
5474 /// Note that unicode in the HostString will be encoded as punycode.
5575 /// </summary>
5676 /// <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>
5778 /// <param name="host">The host portion of the uri normally included in the Host header. This may include the port.</param>
5879 /// <param name="pathBase">The first portion of the request path associated with application root.</param>
5980 /// <param name="path">The portion of the request path that identifies the requested resource.</param>
6081 /// <param name="query">The query, if any.</param>
6182 /// <returns>The combined URI components, properly encoded for use in HTTP headers.</returns>
6283 public static string BuildAbsolute (
6384 CaseHandling handling ,
85+ string scheme ,
6486 HostString host ,
6587 PathString pathBase = default ,
6688 PathString path = default ,
6789 QueryString query = default )
6890 {
91+ Guard . NotNull ( scheme , nameof ( scheme ) ) ;
92+
6993 string hostText = host . ToUriComponent ( ) ;
7094 string pathBaseText = pathBase . ToUriComponent ( ) ;
7195 string pathText = path . ToUriComponent ( ) ;
7296 string queryText = query . ToUriComponent ( ) ;
7397
7498 // PERF: Calculate string length to allocate correct buffer size for string.Create.
7599 int length =
100+ ( scheme . Length > 0 ? scheme . Length + Uri . SchemeDelimiter . Length : 0 ) +
76101 hostText . Length +
77102 pathBaseText . Length +
78103 pathText . Length +
@@ -94,7 +119,10 @@ public static string BuildAbsolute(
94119 length -- ;
95120 }
96121
97- 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 ) ;
98126 }
99127
100128 /// <summary>
@@ -105,7 +133,10 @@ public static string BuildAbsolute(
105133 /// <param name="uri">The Uri to encode.</param>
106134 /// <returns>The encoded string version of <paramref name="uri"/>.</returns>
107135 public static string Encode ( CaseHandling handling , string uri )
108- => Encode ( handling , new Uri ( uri , UriKind . RelativeOrAbsolute ) ) ;
136+ {
137+ Guard . NotNull ( uri , nameof ( uri ) ) ;
138+ return Encode ( handling , new Uri ( uri , UriKind . RelativeOrAbsolute ) ) ;
139+ }
109140
110141 /// <summary>
111142 /// Generates a string from the given absolute or relative Uri that is appropriately encoded for use in
@@ -121,19 +152,18 @@ public static string Encode(CaseHandling handling, Uri uri)
121152 {
122153 return BuildAbsolute (
123154 handling ,
155+ scheme : uri . Scheme ,
124156 host : HostString . FromUriComponent ( uri ) ,
125157 pathBase : PathString . FromUriComponent ( uri ) ,
126158 query : QueryString . FromUriComponent ( uri ) ) ;
127159 }
128160 else
129161 {
130- string components = uri . GetComponents ( UriComponents . SerializationInfoString , UriFormat . UriEscaped ) ;
131- if ( handling == CaseHandling . LowerInvariant )
132- {
133- return components . ToLowerInvariant ( ) ;
134- }
135-
136- return components ;
162+ Uri faux = new ( FallbackBaseUri , uri ) ;
163+ return BuildRelative (
164+ handling ,
165+ path : PathString . FromUriComponent ( faux ) ,
166+ query : QueryString . FromUriComponent ( faux ) ) ;
137167 }
138168 }
139169
@@ -168,7 +198,7 @@ private static int CopyTextToBufferLowerInvariant(Span<char> buffer, int index,
168198 /// </summary>
169199 /// <param name="buffer">The URI <see cref="string"/>'s <see cref="char"/> buffer.</param>
170200 /// <param name="uriParts">The URI parts.</param>
171- private static void InitializeAbsoluteUriString ( Span < char > buffer , ( bool Lower , string Host , string PathBase , string Path , string Query ) uriParts )
201+ private static void InitializeAbsoluteUriString ( Span < char > buffer , ( bool Lower , string Scheme , string Host , string PathBase , string Path , string Query ) uriParts )
172202 {
173203 int index = 0 ;
174204 ReadOnlySpan < char > pathBaseSpan = uriParts . PathBase . AsSpan ( ) ;
@@ -181,6 +211,12 @@ private static void InitializeAbsoluteUriString(Span<char> buffer, (bool Lower,
181211 pathBaseSpan = pathBaseSpan . Slice ( 0 , pathBaseSpan . Length - 1 ) ;
182212 }
183213
214+ if ( uriParts . Scheme . Length > 0 )
215+ {
216+ index = CopyTextToBufferLowerInvariant ( buffer , index , uriParts . Scheme . AsSpan ( ) ) ;
217+ index = CopyTextToBuffer ( buffer , index , Uri . SchemeDelimiter . AsSpan ( ) ) ;
218+ }
219+
184220 if ( uriParts . Lower )
185221 {
186222 index = CopyTextToBufferLowerInvariant ( buffer , index , uriParts . Host . AsSpan ( ) ) ;
0 commit comments