2
2
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3
3
4
4
using System ;
5
+ using System . Buffers ;
5
6
using System . Diagnostics ;
6
7
using System . Runtime . CompilerServices ;
8
+ using System . Runtime . InteropServices ;
7
9
using System . Text ;
8
10
using Microsoft . AspNetCore . Http ;
9
11
using Microsoft . AspNetCore . Server . Kestrel . Core . Internal . Http ;
@@ -25,6 +27,8 @@ internal static partial class HttpUtilities
25
27
private const ulong _http11VersionLong = 3543824036068086856 ; // GetAsciiStringAsLong("HTTP/1.1"); const results in better codegen
26
28
27
29
private static readonly UTF8EncodingSealed HeaderValueEncoding = new UTF8EncodingSealed ( ) ;
30
+ private static readonly SpanAction < char , IntPtr > _getHeaderName = GetHeaderName ;
31
+ private static readonly SpanAction < char , IntPtr > _getAsciiStringNonNullCharacters = GetAsciiStringNonNullCharacters ;
28
32
29
33
[ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
30
34
private static void SetKnownMethod ( ulong mask , ulong knownMethodUlong , HttpMethod knownMethod , int length )
@@ -81,52 +85,61 @@ private static unsafe ulong GetMaskAsLong(byte[] bytes)
81
85
}
82
86
83
87
// The same as GetAsciiStringNonNullCharacters but throws BadRequest
88
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
84
89
public static unsafe string GetHeaderName ( this ReadOnlySpan < byte > span )
85
90
{
86
91
if ( span . IsEmpty )
87
92
{
88
93
return string . Empty ;
89
94
}
90
95
91
- var asciiString = new string ( '\0 ' , span . Length ) ;
96
+ fixed ( byte * source = & MemoryMarshal . GetReference ( span ) )
97
+ {
98
+ return string . Create ( span . Length , new IntPtr ( source ) , _getHeaderName ) ;
99
+ }
100
+ }
92
101
93
- fixed ( char * output = asciiString )
94
- fixed ( byte * buffer = span )
102
+ private static unsafe void GetHeaderName ( Span < char > buffer , IntPtr state )
103
+ {
104
+ fixed ( char * output = & MemoryMarshal . GetReference ( buffer ) )
95
105
{
96
106
// This version if AsciiUtilities returns null if there are any null (0 byte) characters
97
107
// in the string
98
- if ( ! StringUtilities . TryGetAsciiString ( buffer , output , span . Length ) )
108
+ if ( ! StringUtilities . TryGetAsciiString ( ( byte * ) state . ToPointer ( ) , output , buffer . Length ) )
99
109
{
100
110
BadHttpRequestException . Throw ( RequestRejectionReason . InvalidCharactersInHeaderName ) ;
101
111
}
102
112
}
103
-
104
- return asciiString ;
105
113
}
106
114
107
115
public static string GetAsciiStringNonNullCharacters ( this Span < byte > span )
108
116
=> GetAsciiStringNonNullCharacters ( ( ReadOnlySpan < byte > ) span ) ;
109
117
118
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
110
119
public static unsafe string GetAsciiStringNonNullCharacters ( this ReadOnlySpan < byte > span )
111
120
{
112
121
if ( span . IsEmpty )
113
122
{
114
123
return string . Empty ;
115
124
}
116
125
117
- var asciiString = new string ( '\0 ' , span . Length ) ;
126
+ fixed ( byte * source = & MemoryMarshal . GetReference ( span ) )
127
+ {
128
+ return string . Create ( span . Length , new IntPtr ( source ) , _getAsciiStringNonNullCharacters ) ;
129
+ }
130
+ }
118
131
119
- fixed ( char * output = asciiString )
120
- fixed ( byte * buffer = span )
132
+ private static unsafe void GetAsciiStringNonNullCharacters ( Span < char > buffer , IntPtr state )
133
+ {
134
+ fixed ( char * output = & MemoryMarshal . GetReference ( buffer ) )
121
135
{
122
136
// This version if AsciiUtilities returns null if there are any null (0 byte) characters
123
137
// in the string
124
- if ( ! StringUtilities . TryGetAsciiString ( buffer , output , span . Length ) )
138
+ if ( ! StringUtilities . TryGetAsciiString ( ( byte * ) state . ToPointer ( ) , output , buffer . Length ) )
125
139
{
126
140
throw new InvalidOperationException ( ) ;
127
141
}
128
142
}
129
- return asciiString ;
130
143
}
131
144
132
145
public static unsafe string GetAsciiOrUTF8StringNonNullCharacters ( this Span < byte > span )
@@ -139,14 +152,12 @@ public static unsafe string GetAsciiOrUTF8StringNonNullCharacters(this ReadOnlyS
139
152
return string . Empty ;
140
153
}
141
154
142
- var resultString = new string ( '\0 ' , span . Length ) ;
143
-
144
- fixed ( char * output = resultString )
145
- fixed ( byte * buffer = span )
155
+ fixed ( byte * source = & MemoryMarshal . GetReference ( span ) )
146
156
{
147
- // This version if AsciiUtilities returns null if there are any null (0 byte) characters
148
- // in the string
149
- if ( ! StringUtilities . TryGetAsciiString ( buffer , output , span . Length ) )
157
+ var resultString = string . Create ( span . Length , new IntPtr ( source ) , s_getAsciiOrUtf8StringNonNullCharacters ) ;
158
+
159
+ // If resultString is marked, perform UTF-8 encoding
160
+ if ( resultString [ 0 ] == '\0 ' )
150
161
{
151
162
// null characters are considered invalid
152
163
if ( span . IndexOf ( ( byte ) 0 ) != - 1 )
@@ -156,15 +167,32 @@ public static unsafe string GetAsciiOrUTF8StringNonNullCharacters(this ReadOnlyS
156
167
157
168
try
158
169
{
159
- resultString = HeaderValueEncoding . GetString ( buffer , span . Length ) ;
170
+ resultString = HeaderValueEncoding . GetString ( span ) ;
160
171
}
161
172
catch ( DecoderFallbackException )
162
173
{
163
174
throw new InvalidOperationException ( ) ;
164
175
}
165
176
}
177
+
178
+ return resultString ;
179
+ }
180
+ }
181
+
182
+ private static readonly SpanAction < char , IntPtr > s_getAsciiOrUtf8StringNonNullCharacters = GetAsciiOrUTF8StringNonNullCharacters ;
183
+
184
+ private static unsafe void GetAsciiOrUTF8StringNonNullCharacters ( Span < char > buffer , IntPtr state )
185
+ {
186
+ fixed ( char * output = & MemoryMarshal . GetReference ( buffer ) )
187
+ {
188
+ // This version if AsciiUtilities returns null if there are any null (0 byte) characters
189
+ // in the string
190
+ if ( ! StringUtilities . TryGetAsciiString ( ( byte * ) state . ToPointer ( ) , output , buffer . Length ) )
191
+ {
192
+ // Mark resultString for UTF-8 encoding
193
+ output [ 0 ] = '\0 ' ;
194
+ }
166
195
}
167
- return resultString ;
168
196
}
169
197
170
198
public static string GetAsciiStringEscaped ( this Span < byte > span , int maxChars )
@@ -283,7 +311,7 @@ public static HttpMethod GetKnownMethod(string value)
283
311
{
284
312
method = HttpMethod. Head ;
285
313
}
286
- else if ( firstChar == 'P' && string . Equals ( value , HttpMethods . Post , StringComparison . Ordinal ) )
314
+ else if ( firstChar == 'P' && string . Equals ( value , HttpMethods . Post , StringComparison . Ordinal ) )
287
315
{
288
316
method = HttpMethod. Post ;
289
317
}
@@ -294,7 +322,7 @@ public static HttpMethod GetKnownMethod(string value)
294
322
{
295
323
method = HttpMethod. Trace ;
296
324
}
297
- else if ( firstChar == 'P' && string . Equals ( value , HttpMethods . Patch , StringComparison . Ordinal ) )
325
+ else if ( firstChar == 'P' && string . Equals ( value , HttpMethods . Patch , StringComparison . Ordinal ) )
298
326
{
299
327
method = HttpMethod. Patch ;
300
328
}
0 commit comments