Skip to content

Commit 3366d3c

Browse files
committed
Test HTTP/2 trailers are reset
1 parent db14ed6 commit 3366d3c

File tree

1 file changed

+79
-0
lines changed

1 file changed

+79
-0
lines changed

src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using Microsoft.AspNetCore.Connections;
1616
using Microsoft.AspNetCore.Connections.Features;
1717
using Microsoft.AspNetCore.Http;
18+
using Microsoft.AspNetCore.Http.Features;
1819
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
1920
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
2021
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
@@ -209,6 +210,84 @@ await ExpectAsync(Http2FrameType.HEADERS,
209210
await StopConnectionAsync(expectedLastStreamId: 3, ignoreNonGoAwayFrames: false);
210211
}
211212

213+
[Fact]
214+
public async Task ResponseTrailers_MultipleStreams_Reset()
215+
{
216+
IEnumerable<KeyValuePair<string, string>> requestHeaders = new[]
217+
{
218+
new KeyValuePair<string, string>(HeaderNames.Method, "GET"),
219+
new KeyValuePair<string, string>(HeaderNames.Path, "/hello"),
220+
new KeyValuePair<string, string>(HeaderNames.Scheme, "http"),
221+
new KeyValuePair<string, string>(HeaderNames.Authority, "localhost:80"),
222+
new KeyValuePair<string, string>(HeaderNames.ContentType, "application/json")
223+
};
224+
225+
var requestCount = 0;
226+
await InitializeConnectionAsync(context =>
227+
{
228+
requestCount++;
229+
230+
var trailers = context.Features.Get<IHttpResponseTrailersFeature>().Trailers;
231+
trailers["trailer-" + requestCount] = "true";
232+
return Task.CompletedTask;
233+
});
234+
235+
await StartStreamAsync(1, requestHeaders, endStream: true);
236+
237+
await ExpectAsync(Http2FrameType.HEADERS,
238+
withLength: 36,
239+
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS),
240+
withStreamId: 1);
241+
242+
var trailersFrame = await ExpectAsync(Http2FrameType.HEADERS,
243+
withLength: 16,
244+
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
245+
withStreamId: 1);
246+
247+
_hpackDecoder.Decode(trailersFrame.PayloadSequence, endHeaders: true, handler: this);
248+
249+
Assert.Single(_decodedHeaders);
250+
Assert.Equal("true", _decodedHeaders["trailer-1"]);
251+
252+
_decodedHeaders.Clear();
253+
254+
// Ping will trigger the stream to be returned to the pool so we can assert it
255+
await SendPingAsync(Http2PingFrameFlags.NONE);
256+
await ExpectAsync(Http2FrameType.PING,
257+
withLength: 8,
258+
withFlags: (byte)Http2PingFrameFlags.ACK,
259+
withStreamId: 0);
260+
await SendPingAsync(Http2PingFrameFlags.NONE);
261+
await ExpectAsync(Http2FrameType.PING,
262+
withLength: 8,
263+
withFlags: (byte)Http2PingFrameFlags.ACK,
264+
withStreamId: 0);
265+
266+
// Stream has been returned to the pool
267+
Assert.Equal(1, _connection.StreamPool.Count);
268+
269+
await StartStreamAsync(3, requestHeaders, endStream: true);
270+
271+
await ExpectAsync(Http2FrameType.HEADERS,
272+
withLength: 6,
273+
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS),
274+
withStreamId: 3);
275+
276+
trailersFrame = await ExpectAsync(Http2FrameType.HEADERS,
277+
withLength: 16,
278+
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
279+
withStreamId: 3);
280+
281+
_hpackDecoder.Decode(trailersFrame.PayloadSequence, endHeaders: true, handler: this);
282+
283+
Assert.Single(_decodedHeaders);
284+
Assert.Equal("true", _decodedHeaders["trailer-2"]);
285+
286+
_decodedHeaders.Clear();
287+
288+
await StopConnectionAsync(expectedLastStreamId: 3, ignoreNonGoAwayFrames: false);
289+
}
290+
212291
[Fact]
213292
public async Task StreamPool_SingleStream_ReturnedToPool()
214293
{

0 commit comments

Comments
 (0)