1
- using System ;
1
+ using GenHTTP . Api . Protocol ;
2
+ using GenHTTP . Api . Routing ;
3
+ using GenHTTP . Engine . Infrastructure . Configuration ;
4
+ using PooledAwait ;
5
+ using System ;
2
6
using System . Buffers ;
3
7
using System . Collections . Generic ;
4
8
using System . Text ;
5
9
using System . Text . RegularExpressions ;
6
10
using System . Threading . Tasks ;
7
11
8
- using GenHTTP . Api . Protocol ;
9
- using GenHTTP . Api . Routing ;
10
-
11
- using GenHTTP . Engine . Infrastructure . Configuration ;
12
- using PooledAwait ;
13
-
14
12
namespace GenHTTP . Engine . Protocol
15
13
{
16
14
@@ -29,6 +27,8 @@ internal sealed class RequestParser
29
27
private static readonly char [ ] LINE_ENDING = new char [ ] { '\r ' } ;
30
28
private static readonly char [ ] PATH_ENDING = new char [ ] { '\r ' , '\n ' , '?' , ' ' } ;
31
29
30
+ private static readonly string [ ] KNOWN_HEADERS = new [ ] { "Host" , "User-Agent" , "Accept" } ;
31
+
32
32
private static readonly Encoding ASCII = Encoding . ASCII ;
33
33
34
34
private RequestBuilder ? _Builder ;
@@ -52,14 +52,16 @@ internal RequestParser(NetworkConfiguration configuration)
52
52
53
53
#region Functionality
54
54
55
- internal async ValueTask < RequestBuilder ? > TryParseAsync ( RequestBuffer buffer )
55
+ internal async PooledValueTask < RequestBuilder ? > TryParseAsync ( RequestBuffer buffer )
56
56
{
57
57
WebPath ? path ;
58
58
RequestQuery ? query ;
59
59
60
- string ? method , protocol ;
60
+ FlexibleRequestMethod ? method ;
61
61
62
- if ( ( method = await ReadToken ( buffer , ' ' ) . ConfigureAwait ( false ) ) is not null )
62
+ string ? protocol ;
63
+
64
+ if ( ( method = await TryReadMethod ( buffer ) ) is not null )
63
65
{
64
66
Request . Type ( method ) ;
65
67
}
@@ -82,7 +84,7 @@ internal RequestParser(NetworkConfiguration configuration)
82
84
Request . Query ( query ) ;
83
85
}
84
86
85
- if ( ( protocol = await ReadToken ( buffer , ' \r ' , 1 , 5 ) ) is not null )
87
+ if ( ( protocol = await TryReadProtocolVersion ( buffer ) ) is not null )
86
88
{
87
89
Request . Protocol ( protocol ) ;
88
90
}
@@ -121,6 +123,49 @@ internal RequestParser(NetworkConfiguration configuration)
121
123
return result ;
122
124
}
123
125
126
+ private static async PooledValueTask < FlexibleRequestMethod ? > TryReadMethod ( RequestBuffer buffer )
127
+ {
128
+ if ( await FindPosition ( buffer , ' ' ) . ConfigureAwait ( false ) is null )
129
+ {
130
+ return null ;
131
+ }
132
+
133
+ if ( CompareTo ( buffer , "GET" , 1 ) ) return FlexibleRequestMethod . Get ( RequestMethod . GET ) ;
134
+
135
+ var token = await ReadToken ( buffer , ' ' ) ;
136
+
137
+ if ( token is not null )
138
+ {
139
+ return FlexibleRequestMethod . Get ( token ) ;
140
+ }
141
+
142
+ return null ;
143
+ }
144
+
145
+ private static async PooledValueTask < string ? > TryReadProtocolVersion ( RequestBuffer buffer )
146
+ {
147
+ // skip the "HTTP/" part
148
+ buffer . Advance ( 5 ) ;
149
+
150
+ if ( await FindPosition ( buffer , '\r ' ) . ConfigureAwait ( false ) is null )
151
+ {
152
+ return null ;
153
+ }
154
+
155
+ if ( CompareTo ( buffer , "1.1" , 2 ) )
156
+ {
157
+ return "1.1" ;
158
+ }
159
+ else if ( CompareTo ( buffer , "1.0" , 2 ) )
160
+ {
161
+ return "1.0" ;
162
+ }
163
+ else
164
+ {
165
+ return null ;
166
+ }
167
+ }
168
+
124
169
private static async ValueTask < WebPath ? > TryReadPath ( RequestBuffer buffer )
125
170
{
126
171
// ignore the slash at the beginning
@@ -180,41 +225,31 @@ private static async ValueTask<bool> TryReadHeader(RequestBuffer buffer, Request
180
225
{
181
226
string ? key , value ;
182
227
183
- if ( ( key = await ReadToken ( buffer , ':' , 1 ) . ConfigureAwait ( false ) ) is not null )
228
+ if ( await FindPosition ( buffer , ':' ) . ConfigureAwait ( false ) is not null )
184
229
{
185
- if ( ( value = await ReadToken ( buffer , ' \r ' , 1 ) ) is not null )
230
+ foreach ( var knownHeader in KNOWN_HEADERS )
186
231
{
187
- request . Header ( key , value ) ;
188
- return true ;
189
- }
190
- }
191
-
192
- return false ;
193
- }
194
-
195
- private static async PooledValueTask < SequencePosition ? > FindPosition ( RequestBuffer buffer , char delimiter )
196
- {
197
- if ( buffer . ReadRequired )
198
- {
199
- if ( ( await buffer . Read ( ) . ConfigureAwait ( false ) ) is null )
200
- {
201
- return null ;
232
+ if ( CompareTo ( buffer , knownHeader , 2 ) )
233
+ {
234
+ if ( ( value = await ReadToken ( buffer , '\r ' , 1 ) ) is not null )
235
+ {
236
+ request . Header ( knownHeader , value ) ;
237
+ return true ;
238
+ }
239
+ }
202
240
}
203
- }
204
-
205
- var position = buffer . Data . PositionOf ( ( byte ) delimiter ) ;
206
241
207
- if ( position is null )
208
- {
209
- if ( ( await buffer . Read ( true ) . ConfigureAwait ( false ) ) is null )
242
+ if ( ( key = await ReadToken ( buffer , ':' , 1 ) ) is not null )
210
243
{
211
- return null ;
244
+ if ( ( value = await ReadToken ( buffer , '\r ' , 1 ) ) is not null )
245
+ {
246
+ request . Header ( key , value ) ;
247
+ return true ;
248
+ }
212
249
}
213
-
214
- position = buffer . Data . PositionOf ( ( byte ) delimiter ) ;
215
250
}
216
251
217
- return position ;
252
+ return false ;
218
253
}
219
254
220
255
private static ValueTask < string ? > ReadToken ( RequestBuffer buffer , char delimiter , ushort skipNext = 0 , ushort skipFirst = 0 , bool skipDelimiter = true )
@@ -268,6 +303,57 @@ private static async ValueTask<bool> TryReadHeader(RequestBuffer buffer, Request
268
303
return null ;
269
304
}
270
305
306
+ private static bool CompareTo ( RequestBuffer buffer , string expected , ushort skipNext = 0 )
307
+ {
308
+ var i = 0 ;
309
+
310
+ var slice = buffer . Data . Slice ( 0 , expected . Length ) ;
311
+
312
+ if ( slice . Length != expected . Length )
313
+ {
314
+ return false ;
315
+ }
316
+
317
+ foreach ( var segment in slice )
318
+ {
319
+ for ( int j = 0 ; j < segment . Length ; j ++ )
320
+ {
321
+ if ( segment . Span [ j ] != expected [ i ++ ] )
322
+ {
323
+ return false ;
324
+ }
325
+ }
326
+ }
327
+
328
+ buffer . Advance ( expected . Length + skipNext ) ;
329
+ return true ;
330
+ }
331
+
332
+ private static async PooledValueTask < SequencePosition ? > FindPosition ( RequestBuffer buffer , char delimiter )
333
+ {
334
+ if ( buffer . ReadRequired )
335
+ {
336
+ if ( ( await buffer . Read ( ) . ConfigureAwait ( false ) ) is null )
337
+ {
338
+ return null ;
339
+ }
340
+ }
341
+
342
+ var position = buffer . Data . PositionOf ( ( byte ) delimiter ) ;
343
+
344
+ if ( position is null )
345
+ {
346
+ if ( ( await buffer . Read ( true ) . ConfigureAwait ( false ) ) is null )
347
+ {
348
+ return null ;
349
+ }
350
+
351
+ position = buffer . Data . PositionOf ( ( byte ) delimiter ) ;
352
+ }
353
+
354
+ return position ;
355
+ }
356
+
271
357
private static string GetString ( ReadOnlySequence < byte > buffer )
272
358
{
273
359
return string . Create ( ( int ) buffer . Length , buffer , ( span , sequence ) =>
0 commit comments