@@ -4107,5 +4107,178 @@ await InitializeConnectionAsync(async context =>
4107
4107
Assert . Single ( _decodedHeaders ) ;
4108
4108
Assert . Equal ( "Custom Value" , _decodedHeaders [ "CustomName" ] ) ;
4109
4109
}
4110
+
4111
+ [ Fact ]
4112
+ public async Task ResetAfterCompleteAsync_GETWithResponseBodyAndTrailers_ResetsAfterResponse ( )
4113
+ {
4114
+ var startingTcs = new TaskCompletionSource < int > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
4115
+ var appTcs = new TaskCompletionSource < int > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
4116
+ var clientTcs = new TaskCompletionSource < int > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
4117
+ var headers = new [ ]
4118
+ {
4119
+ new KeyValuePair < string , string > ( HeaderNames . Method , "GET" ) ,
4120
+ new KeyValuePair < string , string > ( HeaderNames . Path , "/" ) ,
4121
+ new KeyValuePair < string , string > ( HeaderNames . Scheme , "http" ) ,
4122
+ } ;
4123
+ await InitializeConnectionAsync ( async context =>
4124
+ {
4125
+ try
4126
+ {
4127
+ context . Response . OnStarting ( ( ) => { startingTcs . SetResult ( 0 ) ; return Task . CompletedTask ; } ) ;
4128
+ var completionFeature = context . Features . Get < IHttpResponseCompletionFeature > ( ) ;
4129
+ Assert . NotNull ( completionFeature ) ;
4130
+
4131
+ await context . Response . WriteAsync ( "Hello World" ) ;
4132
+ Assert . True ( startingTcs . Task . IsCompletedSuccessfully ) ; // OnStarting got called.
4133
+ Assert . True ( context . Response . Headers . IsReadOnly ) ;
4134
+
4135
+ context . Response . AppendTrailer ( "CustomName" , "Custom Value" ) ;
4136
+
4137
+ await completionFeature . CompleteAsync ( ) . DefaultTimeout ( ) ;
4138
+
4139
+ Assert . True ( context . Features . Get < IHttpResponseTrailersFeature > ( ) . Trailers . IsReadOnly ) ;
4140
+
4141
+ // RequestAborted will no longer fire after CompleteAsync.
4142
+ Assert . False ( context . RequestAborted . CanBeCanceled ) ;
4143
+ var resetFeature = context . Features . Get < IHttpResetFeature > ( ) ;
4144
+ Assert . NotNull ( resetFeature ) ;
4145
+ resetFeature . Reset ( ( int ) Http2ErrorCode . NO_ERROR ) ;
4146
+
4147
+ // Make sure the client gets our results from CompleteAsync instead of from the request delegate exiting.
4148
+ await clientTcs . Task . DefaultTimeout ( ) ;
4149
+ appTcs . SetResult ( 0 ) ;
4150
+ }
4151
+ catch ( Exception ex )
4152
+ {
4153
+ appTcs . SetException ( ex ) ;
4154
+ }
4155
+ } ) ;
4156
+
4157
+ await StartStreamAsync ( 1 , headers , endStream : true ) ;
4158
+
4159
+ var headersFrame = await ExpectAsync ( Http2FrameType . HEADERS ,
4160
+ withLength : 37 ,
4161
+ withFlags : ( byte ) ( Http2HeadersFrameFlags . END_HEADERS ) ,
4162
+ withStreamId : 1 ) ;
4163
+ var bodyFrame = await ExpectAsync ( Http2FrameType . DATA ,
4164
+ withLength : 11 ,
4165
+ withFlags : ( byte ) ( Http2HeadersFrameFlags . NONE ) ,
4166
+ withStreamId : 1 ) ;
4167
+ var trailersFrame = await ExpectAsync ( Http2FrameType . HEADERS ,
4168
+ withLength : 25 ,
4169
+ withFlags : ( byte ) ( Http2HeadersFrameFlags . END_HEADERS | Http2HeadersFrameFlags . END_STREAM ) ,
4170
+ withStreamId : 1 ) ;
4171
+ await WaitForStreamErrorAsync ( 1 , Http2ErrorCode . NO_ERROR , expectedErrorMessage :
4172
+ "The HTTP/2 stream was reset by the application with error code NO_ERROR." ) ;
4173
+
4174
+ clientTcs . SetResult ( 0 ) ;
4175
+ await appTcs . Task ;
4176
+
4177
+ await StopConnectionAsync ( expectedLastStreamId : 1 , ignoreNonGoAwayFrames : false ) ;
4178
+
4179
+ _hpackDecoder . Decode ( headersFrame . PayloadSequence , endHeaders : false , handler : this ) ;
4180
+
4181
+ Assert . Equal ( 2 , _decodedHeaders . Count ) ;
4182
+ Assert . Contains ( "date" , _decodedHeaders . Keys , StringComparer . OrdinalIgnoreCase ) ;
4183
+ Assert . Equal ( "200" , _decodedHeaders [ HeaderNames . Status ] ) ;
4184
+
4185
+ Assert . Equal ( "Hello World" , Encoding . UTF8 . GetString ( bodyFrame . Payload . Span ) ) ;
4186
+
4187
+ _decodedHeaders . Clear ( ) ;
4188
+
4189
+ _hpackDecoder . Decode ( trailersFrame . PayloadSequence , endHeaders : true , handler : this ) ;
4190
+
4191
+ Assert . Single ( _decodedHeaders ) ;
4192
+ Assert . Equal ( "Custom Value" , _decodedHeaders [ "CustomName" ] ) ;
4193
+ }
4194
+
4195
+ [ Fact ]
4196
+ public async Task ResetAfterCompleteAsync_POSTWithResponseBodyAndTrailers_RequestBodyThrows ( )
4197
+ {
4198
+ var startingTcs = new TaskCompletionSource < int > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
4199
+ var appTcs = new TaskCompletionSource < int > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
4200
+ var clientTcs = new TaskCompletionSource < int > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
4201
+ var headers = new [ ]
4202
+ {
4203
+ new KeyValuePair < string , string > ( HeaderNames . Method , "POST" ) ,
4204
+ new KeyValuePair < string , string > ( HeaderNames . Path , "/" ) ,
4205
+ new KeyValuePair < string , string > ( HeaderNames . Scheme , "http" ) ,
4206
+ } ;
4207
+ await InitializeConnectionAsync ( async context =>
4208
+ {
4209
+ try
4210
+ {
4211
+ var requestBodyTask = context . Request . BodyReader . ReadAsync ( ) ;
4212
+
4213
+ context . Response . OnStarting ( ( ) => { startingTcs . SetResult ( 0 ) ; return Task . CompletedTask ; } ) ;
4214
+ var completionFeature = context . Features . Get < IHttpResponseCompletionFeature > ( ) ;
4215
+ Assert . NotNull ( completionFeature ) ;
4216
+
4217
+ await context . Response . WriteAsync ( "Hello World" ) ;
4218
+ Assert . True ( startingTcs . Task . IsCompletedSuccessfully ) ; // OnStarting got called.
4219
+ Assert . True ( context . Response . Headers . IsReadOnly ) ;
4220
+
4221
+ context . Response . AppendTrailer ( "CustomName" , "Custom Value" ) ;
4222
+
4223
+ await completionFeature . CompleteAsync ( ) . DefaultTimeout ( ) ;
4224
+
4225
+ Assert . True ( context . Features . Get < IHttpResponseTrailersFeature > ( ) . Trailers . IsReadOnly ) ;
4226
+
4227
+ // RequestAborted will no longer fire after CompleteAsync.
4228
+ Assert . False ( context . RequestAborted . CanBeCanceled ) ;
4229
+ var resetFeature = context . Features . Get < IHttpResetFeature > ( ) ;
4230
+ Assert . NotNull ( resetFeature ) ;
4231
+ resetFeature . Reset ( ( int ) Http2ErrorCode . NO_ERROR ) ;
4232
+
4233
+ await Assert . ThrowsAsync < TaskCanceledException > ( async ( ) => await requestBodyTask ) ;
4234
+ await Assert . ThrowsAsync < ConnectionAbortedException > ( async ( ) => await context . Request . BodyReader . ReadAsync ( ) ) ;
4235
+
4236
+ // Make sure the client gets our results from CompleteAsync instead of from the request delegate exiting.
4237
+ await clientTcs . Task . DefaultTimeout ( ) ;
4238
+ appTcs . SetResult ( 0 ) ;
4239
+ }
4240
+ catch ( Exception ex )
4241
+ {
4242
+ appTcs . SetException ( ex ) ;
4243
+ }
4244
+ } ) ;
4245
+
4246
+ await StartStreamAsync ( 1 , headers , endStream : false ) ;
4247
+
4248
+ var headersFrame = await ExpectAsync ( Http2FrameType . HEADERS ,
4249
+ withLength : 37 ,
4250
+ withFlags : ( byte ) ( Http2HeadersFrameFlags . END_HEADERS ) ,
4251
+ withStreamId : 1 ) ;
4252
+ var bodyFrame = await ExpectAsync ( Http2FrameType . DATA ,
4253
+ withLength : 11 ,
4254
+ withFlags : ( byte ) ( Http2HeadersFrameFlags . NONE ) ,
4255
+ withStreamId : 1 ) ;
4256
+ var trailersFrame = await ExpectAsync ( Http2FrameType . HEADERS ,
4257
+ withLength : 25 ,
4258
+ withFlags : ( byte ) ( Http2HeadersFrameFlags . END_HEADERS | Http2HeadersFrameFlags . END_STREAM ) ,
4259
+ withStreamId : 1 ) ;
4260
+ await WaitForStreamErrorAsync ( 1 , Http2ErrorCode . NO_ERROR , expectedErrorMessage :
4261
+ "The HTTP/2 stream was reset by the application with error code NO_ERROR." ) ;
4262
+
4263
+ clientTcs . SetResult ( 0 ) ;
4264
+ await appTcs . Task ;
4265
+
4266
+ await StopConnectionAsync ( expectedLastStreamId : 1 , ignoreNonGoAwayFrames : false ) ;
4267
+
4268
+ _hpackDecoder . Decode ( headersFrame . PayloadSequence , endHeaders : false , handler : this ) ;
4269
+
4270
+ Assert . Equal ( 2 , _decodedHeaders . Count ) ;
4271
+ Assert . Contains ( "date" , _decodedHeaders . Keys , StringComparer . OrdinalIgnoreCase ) ;
4272
+ Assert . Equal ( "200" , _decodedHeaders [ HeaderNames . Status ] ) ;
4273
+
4274
+ Assert . Equal ( "Hello World" , Encoding . UTF8 . GetString ( bodyFrame . Payload . Span ) ) ;
4275
+
4276
+ _decodedHeaders . Clear ( ) ;
4277
+
4278
+ _hpackDecoder . Decode ( trailersFrame . PayloadSequence , endHeaders : true , handler : this ) ;
4279
+
4280
+ Assert . Single ( _decodedHeaders ) ;
4281
+ Assert . Equal ( "Custom Value" , _decodedHeaders [ "CustomName" ] ) ;
4282
+ }
4110
4283
}
4111
4284
}
0 commit comments