Skip to content

Commit 9294020

Browse files
author
ladeak
committed
Add ChromiumProcessFrames test and helper methods
Introduce ChromiumProcessFrames test to simulate Chromium's control stream frames and verify server handling. Add WriteSettings, WriteGoAway, and ReadSettigsAsync helpers to streamline frame operations and refactor test code for clarity.
1 parent 2f0d8b0 commit 9294020

2 files changed

Lines changed: 67 additions & 7 deletions

File tree

tests/CHttpServer.Tests/Http2ConnectionTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,7 @@ public async Task UseHttp3_ReturnsAltSvcHeader()
718718

719719
// Assert response
720720
var (frame, headers) = await AssertResponseHeaders(pipe);
721-
Assert.True(headers.TryGetValue("alt-svc", out var altSvc) && altSvc == "h3=\":443\"");
721+
Assert.True(headers.TryGetValue("alt-svc", out var altSvc) && altSvc == "h3=\":443\"; ma=86400");
722722
Assert.True(frame.EndHeaders);
723723
await AssertEmptyEndStream(pipe);
724724

tests/CHttpServer.Tests/Http3/Http3ConnectionTests.cs

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ public async Task InvalidFrameTypeOnControlStream_Aborts(int frameType)
131131
await AssertGoAwayAsync(controlStream, 2);
132132
}, TestContext.Current.CancellationToken);
133133
var clientControlStream = await fixture.ClientConnection.OpenOutboundStreamAsync(QuicStreamType.Unidirectional, TestContext.Current.CancellationToken);
134+
await WriteSettings(clientControlStream);
134135

135136
// StreamType: 0-control, FrameType: 1-Invalid, Length: 0
136137
byte[] data = [0, (byte)frameType, 0];
@@ -158,20 +159,16 @@ public async Task UnknownFrameType_OnControlStream_ISgnored(int frameType)
158159
await AssertGoAwayAsync(controlStream, 2);
159160
}, TestContext.Current.CancellationToken);
160161
var clientControlStream = await fixture.ClientConnection.OpenOutboundStreamAsync(QuicStreamType.Unidirectional, TestContext.Current.CancellationToken);
162+
await WriteSettings(clientControlStream);
161163

162164
// StreamType: 0-control, FrameType, Length: 0
163165
Span<byte> buffer = stackalloc byte[8];
164166
VariableLenghtIntegerDecoder.TryWrite(buffer, frameType, out var length);
165-
166167
byte[] data = [0, .. buffer[..length], 0];
167168
await clientControlStream.WriteAsync(data, TestContext.Current.CancellationToken);
168169
await clientControlStream.FlushAsync(TestContext.Current.CancellationToken);
169170

170-
// Write GOAWAY FrameType: 7, Length: 1, StreamId: 0
171-
data = [7, 1, 0];
172-
await clientControlStream.WriteAsync(data, TestContext.Current.CancellationToken);
173-
await clientControlStream.FlushAsync(TestContext.Current.CancellationToken);
174-
171+
await WriteGoAway(clientControlStream);
175172
await readServerControlStream;
176173
await processing;
177174
}
@@ -305,6 +302,32 @@ await sut.ProcessConnectionAsync(new TestBase.TestApplication(_ => Task.Complete
305302
await readServerControlStream;
306303
}
307304

305+
[Fact]
306+
public async Task ChromiumProcessFrames()
307+
{
308+
await using var fixture = await QuicConnectionFixture.SetupConnectionAsync(Port, TestContext.Current.CancellationToken);
309+
Http3Connection sut = CreateHttp3Connection(fixture.ServerConnection);
310+
var processing = sut.ProcessConnectionAsync(new TestBase.TestApplication(_ => Task.CompletedTask))
311+
.WaitAsync(TimeSpan.FromSeconds(10), TestContext.Current.CancellationToken);
312+
var readServerControlStream = Task.Run(async () =>
313+
{
314+
var controlStream = await fixture.ClientConnection.AcceptInboundStreamAsync(TestContext.Current.CancellationToken);
315+
await AssertReadSettigsAsync(controlStream);
316+
await AssertGoAwayAsync(controlStream, 2);
317+
}, TestContext.Current.CancellationToken);
318+
var clientControlStream = await fixture.ClientConnection.OpenOutboundStreamAsync(QuicStreamType.Unidirectional, TestContext.Current.CancellationToken);
319+
320+
// Chromium control stream data: Settings Frame, Grease, Priorty Frames (Unknown)
321+
byte[] data = [0, 4, 27, 1, 128, 1, 0, 0, 6, 128, 4, 0, 0, 7, 64, 100, 51, 1, 192, 0, 0, 30, 67, 87, 179, 56, 154, 124, 38, 21, 192, 0, 0, 20, 86, 88, 150, 239, 2, 240, 76, 128, 15, 7, 0, 7, 0, 117, 61, 48, 44, 32, 105];
322+
await clientControlStream.WriteAsync(data, TestContext.Current.CancellationToken);
323+
await clientControlStream.FlushAsync(TestContext.Current.CancellationToken);
324+
325+
await WriteGoAway(clientControlStream);
326+
await readServerControlStream;
327+
await processing;
328+
Assert.Equal(262144ul, sut.ClientMaxFieldSectionSize);
329+
}
330+
308331
private static Http3Connection CreateHttp3Connection(QuicConnection serverConnection, CHttpServerOptions? options = null, CancellationTokenSource? connectionCancellation = null)
309332
{
310333
var connectionContext = new CHttp3ConnectionContext()
@@ -343,12 +366,49 @@ private static async Task AssertReadSettigsAsync(Stream stream, byte expectedSet
343366
encodedValue.SequenceEqual(buffer[4..]);
344367
}
345368

369+
private static async Task<IEnumerable<KeyValuePair<long, long>>> ReadSettigsAsync(Stream stream, CancellationToken token = default)
370+
{
371+
var headerBuffer = new byte[10];
372+
int readCount = await stream.ReadAtLeastAsync(headerBuffer, headerBuffer.Length, true, token);
373+
Assert.Equal(0x00, headerBuffer[0]); // Control Stream Type
374+
Assert.Equal(0x04, headerBuffer[1]); // Frame Type: SETTINGS
375+
if (!VariableLenghtIntegerDecoder.TryRead(headerBuffer[2..readCount], out var payloadLength, out var bytesCount))
376+
Assert.Fail();
377+
378+
var payloadBuffer = new byte[payloadLength];
379+
headerBuffer.AsSpan()[(2 + bytesCount)..readCount].CopyTo(payloadBuffer);
380+
var remainingPayloadLength = readCount - 2 - bytesCount;
381+
await stream.ReadExactlyAsync(payloadBuffer.AsMemory(remainingPayloadLength));
382+
383+
var data = payloadBuffer.AsSpan();
384+
List<KeyValuePair<long, long>> result = [];
385+
while (!data.IsEmpty)
386+
{
387+
if (!VariableLenghtIntegerDecoder.TryRead(data, out var settingIdentifier, out bytesCount))
388+
Assert.Fail();
389+
data = data.Slice(bytesCount);
390+
if (!VariableLenghtIntegerDecoder.TryRead(data, out var settingValue, out bytesCount))
391+
Assert.Fail();
392+
data = data.Slice(bytesCount);
393+
result.Add(new((long)settingIdentifier, (long)settingValue));
394+
}
395+
return result;
396+
}
397+
346398
private static async Task WriteSettings(QuicStream clientControlStream)
347399
{
348400
// StreamType: 0-control, FrameType: 4-Settings, Length: 2, Setting Identifier: 33-Reserved, Value: 0
349401
byte[] data = [0, 4, 2, 33, 0];
350402
await clientControlStream.WriteAsync(data, TestContext.Current.CancellationToken);
351403
await clientControlStream.FlushAsync(TestContext.Current.CancellationToken);
352404
}
405+
406+
private static async Task WriteGoAway(QuicStream clientControlStream)
407+
{
408+
// Write GOAWAY FrameType: 7, Length: 1, StreamId: 0
409+
byte[] data = [7, 1, 0];
410+
await clientControlStream.WriteAsync(data, TestContext.Current.CancellationToken);
411+
await clientControlStream.FlushAsync(TestContext.Current.CancellationToken);
412+
}
353413
}
354414

0 commit comments

Comments
 (0)