@@ -32,6 +32,9 @@ internal abstract partial class Http3Stream : HttpProtocol, IHttpHeadersHandler,
32
32
private static ReadOnlySpan < byte > TrailersBytes => new byte [ 8 ] { ( byte ) 't' , ( byte ) 'r' , ( byte ) 'a' , ( byte ) 'i' , ( byte ) 'l' , ( byte ) 'e' , ( byte ) 'r' , ( byte ) 's' } ;
33
33
private static ReadOnlySpan < byte > ConnectBytes => new byte [ 7 ] { ( byte ) 'C' , ( byte ) 'O' , ( byte ) 'N' , ( byte ) 'N' , ( byte ) 'E' , ( byte ) 'C' , ( byte ) 'T' } ;
34
34
35
+ private static readonly PseudoHeaderFields _mandatoryRequestPseudoHeaderFields =
36
+ PseudoHeaderFields . Method | PseudoHeaderFields . Path | PseudoHeaderFields . Scheme ;
37
+
35
38
private readonly Http3FrameWriter _frameWriter ;
36
39
private readonly Http3OutputProducer _http3Output ;
37
40
private int _isClosed ;
@@ -42,6 +45,7 @@ internal abstract partial class Http3Stream : HttpProtocol, IHttpHeadersHandler,
42
45
private readonly Http3RawFrame _incomingFrame = new Http3RawFrame ( ) ;
43
46
protected RequestHeaderParsingState _requestHeaderParsingState ;
44
47
private PseudoHeaderFields _parsedPseudoHeaderFields ;
48
+ private int _totalParsedHeaderSize ;
45
49
private bool _isMethodConnect ;
46
50
47
51
private readonly Http3Connection _http3Connection ;
@@ -148,7 +152,14 @@ public void OnStaticIndexedHeader(int index, ReadOnlySpan<byte> value)
148
152
149
153
public override void OnHeader ( ReadOnlySpan < byte > name , ReadOnlySpan < byte > value )
150
154
{
151
- // TODO MaxRequestHeadersTotalSize?
155
+ // https://tools.ietf.org/html/rfc7540#section-6.5.2
156
+ // "The value is based on the uncompressed size of header fields, including the length of the name and value in octets plus an overhead of 32 octets for each header field.";
157
+ _totalParsedHeaderSize += HeaderField . RfcOverhead + name . Length + value . Length ;
158
+ if ( _totalParsedHeaderSize > _context . ServiceContext . ServerOptions . Limits . MaxRequestHeadersTotalSize )
159
+ {
160
+ throw new Http3StreamErrorException ( CoreStrings . BadRequest_HeadersExceedMaxTotalSize , Http3ErrorCode . RequestRejected ) ;
161
+ }
162
+
152
163
ValidateHeader ( name , value ) ;
153
164
try
154
165
{
@@ -165,23 +176,22 @@ public override void OnHeader(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
165
176
}
166
177
catch ( Microsoft . AspNetCore . Http . BadHttpRequestException bre )
167
178
{
168
- throw new Http3StreamErrorException ( bre . Message , Http3ErrorCode . ProtocolError ) ;
179
+ throw new Http3StreamErrorException ( bre . Message , Http3ErrorCode . MessageError ) ;
169
180
}
170
181
catch ( InvalidOperationException )
171
182
{
172
- throw new Http3StreamErrorException ( CoreStrings . BadRequest_MalformedRequestInvalidHeaders , Http3ErrorCode . ProtocolError ) ;
183
+ throw new Http3StreamErrorException ( CoreStrings . BadRequest_MalformedRequestInvalidHeaders , Http3ErrorCode . MessageError ) ;
173
184
}
174
185
}
175
186
176
187
private void ValidateHeader ( ReadOnlySpan < byte > name , ReadOnlySpan < byte > value )
177
188
{
178
189
// http://httpwg.org/specs/rfc7540.html#rfc.section.8.1.2.1
179
190
/*
180
- Intermediaries that process HTTP requests or responses (i.e., any
181
- intermediary not acting as a tunnel) MUST NOT forward a malformed
182
- request or response. Malformed requests or responses that are
183
- detected MUST be treated as a stream error (Section 5.4.2) of type
184
- PROTOCOL_ERROR.
191
+ Intermediaries that process HTTP requests or responses
192
+ (i.e., any intermediary not acting as a tunnel) MUST NOT forward a
193
+ malformed request or response. Malformed requests or responses that
194
+ are detected MUST be treated as a stream error of type H3_MESSAGE_ERROR.
185
195
186
196
For malformed requests, a server MAY send an HTTP response prior to
187
197
closing or resetting the stream. Clients MUST NOT accept a malformed
@@ -193,39 +203,43 @@ implementations to these vulnerabilities.*/
193
203
{
194
204
if ( _requestHeaderParsingState == RequestHeaderParsingState . Headers )
195
205
{
206
+ // https://quicwg.org/base-drafts/draft-ietf-quic-http.html#section-4.1.1.1-4
196
207
// All pseudo-header fields MUST appear in the header block before regular header fields.
197
208
// Any request or response that contains a pseudo-header field that appears in a header
198
- // block after a regular header field MUST be treated as malformed (Section 8.1.2.6) .
199
- throw new Http3StreamErrorException ( CoreStrings . Http2ErrorPseudoHeaderFieldAfterRegularHeaders , Http3ErrorCode . ProtocolError ) ;
209
+ // block after a regular header field MUST be treated as malformed.
210
+ throw new Http3StreamErrorException ( CoreStrings . HttpErrorPseudoHeaderFieldAfterRegularHeaders , Http3ErrorCode . MessageError ) ;
200
211
}
201
212
202
213
if ( _requestHeaderParsingState == RequestHeaderParsingState . Trailers )
203
214
{
215
+ // https://quicwg.org/base-drafts/draft-ietf-quic-http.html#section-4.1.1.1-3
204
216
// Pseudo-header fields MUST NOT appear in trailers.
205
- throw new Http3StreamErrorException ( CoreStrings . Http2ErrorTrailersContainPseudoHeaderField , Http3ErrorCode . ProtocolError ) ;
217
+ throw new Http3StreamErrorException ( CoreStrings . HttpErrorTrailersContainPseudoHeaderField , Http3ErrorCode . MessageError ) ;
206
218
}
207
219
208
220
_requestHeaderParsingState = RequestHeaderParsingState . PseudoHeaderFields ;
209
221
210
222
if ( headerField == PseudoHeaderFields . Unknown )
211
223
{
224
+ // https://quicwg.org/base-drafts/draft-ietf-quic-http.html#section-4.1.1.1-3
212
225
// Endpoints MUST treat a request or response that contains undefined or invalid pseudo-header
213
- // fields as malformed (Section 8.1.2.6) .
214
- throw new Http3StreamErrorException ( CoreStrings . Http2ErrorUnknownPseudoHeaderField , Http3ErrorCode . ProtocolError ) ;
226
+ // fields as malformed.
227
+ throw new Http3StreamErrorException ( CoreStrings . HttpErrorUnknownPseudoHeaderField , Http3ErrorCode . MessageError ) ;
215
228
}
216
229
217
230
if ( headerField == PseudoHeaderFields . Status )
218
231
{
232
+ // https://quicwg.org/base-drafts/draft-ietf-quic-http.html#section-4.1.1.1-3
219
233
// Pseudo-header fields defined for requests MUST NOT appear in responses; pseudo-header fields
220
234
// defined for responses MUST NOT appear in requests.
221
- throw new Http3StreamErrorException ( CoreStrings . Http2ErrorResponsePseudoHeaderField , Http3ErrorCode . ProtocolError ) ;
235
+ throw new Http3StreamErrorException ( CoreStrings . HttpErrorResponsePseudoHeaderField , Http3ErrorCode . MessageError ) ;
222
236
}
223
237
224
238
if ( ( _parsedPseudoHeaderFields & headerField ) == headerField )
225
239
{
226
- // http ://httpwg .org/specs/rfc7540 .html#rfc. section.8 .1.2.3
227
- // All HTTP/2 requests MUST include exactly one valid value for the :method, :scheme, and :path pseudo-header fields
228
- throw new Http3StreamErrorException ( CoreStrings . Http2ErrorDuplicatePseudoHeaderField , Http3ErrorCode . ProtocolError ) ;
240
+ // https ://quicwg .org/base-drafts/draft-ietf-quic-http .html#section-4.1 .1.1-7
241
+ // All HTTP/3 requests MUST include exactly one valid value for the :method, :scheme, and :path pseudo-header fields
242
+ throw new Http3StreamErrorException ( CoreStrings . HttpErrorDuplicatePseudoHeaderField , Http3ErrorCode . MessageError ) ;
229
243
}
230
244
231
245
if ( headerField == PseudoHeaderFields . Method )
@@ -242,22 +256,22 @@ implementations to these vulnerabilities.*/
242
256
243
257
if ( IsConnectionSpecificHeaderField ( name , value ) )
244
258
{
245
- throw new Http3StreamErrorException ( CoreStrings . Http2ErrorConnectionSpecificHeaderField , Http3ErrorCode . ProtocolError ) ;
259
+ throw new Http3StreamErrorException ( CoreStrings . HttpErrorConnectionSpecificHeaderField , Http3ErrorCode . MessageError ) ;
246
260
}
247
261
248
- // http ://httpwg .org/specs/rfc7540 .html#rfc. section.8.1.2
249
- // A request or response containing uppercase header field names MUST be treated as malformed (Section 8.1.2.6) .
262
+ // https ://quicwg .org/base-drafts/draft-ietf-quic-http .html#section-4.1.1-3
263
+ // A request or response containing uppercase header field names MUST be treated as malformed.
250
264
for ( var i = 0 ; i < name . Length ; i ++ )
251
265
{
252
266
if ( name [ i ] >= 65 && name [ i ] <= 90 )
253
267
{
254
268
if ( _requestHeaderParsingState == RequestHeaderParsingState . Trailers )
255
269
{
256
- throw new Http3StreamErrorException ( CoreStrings . Http2ErrorTrailerNameUppercase , Http3ErrorCode . ProtocolError ) ;
270
+ throw new Http3StreamErrorException ( CoreStrings . HttpErrorTrailerNameUppercase , Http3ErrorCode . MessageError ) ;
257
271
}
258
272
else
259
273
{
260
- throw new Http3StreamErrorException ( CoreStrings . Http2ErrorHeaderNameUppercase , Http3ErrorCode . ProtocolError ) ;
274
+ throw new Http3StreamErrorException ( CoreStrings . HttpErrorHeaderNameUppercase , Http3ErrorCode . MessageError ) ;
261
275
}
262
276
}
263
277
}
@@ -521,6 +535,15 @@ private async Task ProcessHeadersFrameAsync<TContext>(IHttpApplication<TContext>
521
535
await OnEndStreamReceived ( ) ;
522
536
}
523
537
538
+ if ( ! _isMethodConnect && ( _parsedPseudoHeaderFields & _mandatoryRequestPseudoHeaderFields ) != _mandatoryRequestPseudoHeaderFields )
539
+ {
540
+ // All HTTP/3 requests MUST include exactly one valid value for the :method, :scheme, and :path pseudo-header
541
+ // fields, unless it is a CONNECT request. An HTTP request that omits mandatory pseudo-header
542
+ // fields is malformed.
543
+ // https://quicwg.org/base-drafts/draft-ietf-quic-http.html#section-4.1.1.1
544
+ throw new Http3StreamErrorException ( CoreStrings . HttpErrorMissingMandatoryPseudoHeaderFields , Http3ErrorCode . MessageError ) ;
545
+ }
546
+
524
547
_appCompleted = new TaskCompletionSource ( ) ;
525
548
526
549
ThreadPool . UnsafeQueueUserWorkItem ( this , preferLocal : false ) ;
0 commit comments