Skip to content

Commit d588c92

Browse files
committed
More test
1 parent 6af59d8 commit d588c92

File tree

4 files changed

+80
-44
lines changed

4 files changed

+80
-44
lines changed

src/Servers/Kestrel/Core/src/CoreStrings.resx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -677,4 +677,7 @@ For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?l
677677
<data name="Http3ErrorControlStreamReservedSetting" xml:space="preserve">
678678
<value>The client sent a reserved setting identifier: {identifier}</value>
679679
</data>
680-
</root>
680+
<data name="Http3ControlStreamErrorMultipleInboundStreams" xml:space="preserve">
681+
<value>The client created multiple inbound {streamType} streams for the connection.</value>
682+
</data>
683+
</root>

src/Servers/Kestrel/Core/src/Internal/Http3/Http3ControlStream.cs

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -147,45 +147,53 @@ private async ValueTask<long> TryReadStreamIdAsync()
147147

148148
public async Task ProcessRequestAsync<TContext>(IHttpApplication<TContext> application) where TContext : notnull
149149
{
150-
var streamType = await TryReadStreamIdAsync();
151-
152-
if (streamType == -1)
150+
try
153151
{
154-
return;
155-
}
152+
var streamType = await TryReadStreamIdAsync();
156153

157-
if (streamType == ControlStream)
158-
{
159-
if (!_http3Connection.SetInboundControlStream(this))
154+
if (streamType == -1)
160155
{
161-
// https://quicwg.org/base-drafts/draft-ietf-quic-http.html#section-6.2.1
162-
throw new Http3ConnectionErrorException("Only one inbound control stream per peer is permitted.", Http3ErrorCode.StreamCreationError);
156+
return;
163157
}
164158

165-
await HandleControlStream();
166-
}
167-
else if (streamType == EncoderStream)
168-
{
169-
if (!_http3Connection.SetInboundEncoderStream(this))
159+
if (streamType == ControlStream)
170160
{
171-
// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#section-4.2
172-
throw new Http3ConnectionErrorException("Only one inbound encoder stream per peer is permitted.", Http3ErrorCode.StreamCreationError);
161+
if (!_http3Connection.SetInboundControlStream(this))
162+
{
163+
// https://quicwg.org/base-drafts/draft-ietf-quic-http.html#section-6.2.1
164+
throw new Http3ConnectionErrorException(CoreStrings.FormatHttp3ControlStreamErrorMultipleInboundStreams("control"), Http3ErrorCode.StreamCreationError);
165+
}
166+
167+
await HandleControlStream();
173168
}
169+
else if (streamType == EncoderStream)
170+
{
171+
if (!_http3Connection.SetInboundEncoderStream(this))
172+
{
173+
// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#section-4.2
174+
throw new Http3ConnectionErrorException(CoreStrings.FormatHttp3ControlStreamErrorMultipleInboundStreams("encoder"), Http3ErrorCode.StreamCreationError);
175+
}
174176

175-
await HandleEncodingDecodingTask();
176-
}
177-
else if (streamType == DecoderStream)
178-
{
179-
if (!_http3Connection.SetInboundDecoderStream(this))
177+
await HandleEncodingDecodingTask();
178+
}
179+
else if (streamType == DecoderStream)
180+
{
181+
if (!_http3Connection.SetInboundDecoderStream(this))
182+
{
183+
// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#section-4.2
184+
throw new Http3ConnectionErrorException(CoreStrings.FormatHttp3ControlStreamErrorMultipleInboundStreams("decoder"), Http3ErrorCode.StreamCreationError);
185+
}
186+
await HandleEncodingDecodingTask();
187+
}
188+
else
180189
{
181-
// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#section-4.2
182-
throw new Http3ConnectionErrorException("Only one inbound decoder stream per peer is permitted.", Http3ErrorCode.StreamCreationError);
190+
// TODO Close the control stream as it's unexpected.
183191
}
184-
await HandleEncodingDecodingTask();
185192
}
186-
else
193+
catch (Http3ConnectionErrorException ex)
187194
{
188-
// TODO Close the control stream as it's unexpected.
195+
Log.Http3ConnectionError(_http3Connection.ConnectionId, ex);
196+
_http3Connection.Abort(new ConnectionAbortedException(ex.Message, ex), ex.ErrorCode);
189197
}
190198
}
191199

src/Servers/Kestrel/test/InMemory.FunctionalTests/Http3/Http3ConnectionTests.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,25 @@ await WaitForConnectionErrorAsync<Http3ConnectionErrorException>(
9090
ignoreNonGoAwayFrames: true,
9191
expectedLastStreamId: 0,
9292
expectedErrorCode: Http3ErrorCode.SettingsError,
93-
expectedErrorMessage: $"HTTP/3 connection error (SettingsError): The client sent a reserved setting identifier: 0x{settingIdentifier.ToString("X", CultureInfo.InvariantCulture)}");
93+
expectedErrorMessage: CoreStrings.FormatHttp3ErrorControlStreamReservedSetting($"0x{settingIdentifier.ToString("X", CultureInfo.InvariantCulture)}"));
94+
}
95+
96+
[Theory]
97+
[InlineData(0, "control")]
98+
[InlineData(2, "encoder")]
99+
[InlineData(3, "decoder")]
100+
public async Task InboundStreams_CreateMultiple_ConnectionError(int streamId, string name)
101+
{
102+
await InitializeConnectionAsync(_noopApplication);
103+
104+
await CreateControlStream(streamId);
105+
await CreateControlStream(streamId);
106+
107+
await WaitForConnectionErrorAsync<Http3ConnectionErrorException>(
108+
ignoreNonGoAwayFrames: true,
109+
expectedLastStreamId: 0,
110+
expectedErrorCode: Http3ErrorCode.StreamCreationError,
111+
expectedErrorMessage: CoreStrings.FormatHttp3ControlStreamErrorMultipleInboundStreams(name));
94112
}
95113
}
96114
}

src/Servers/Kestrel/test/InMemory.FunctionalTests/Http3/Http3TestBase.cs

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -121,20 +121,27 @@ internal async ValueTask<Http3ControlStream> GetInboundControlStream()
121121
{
122122
if (_inboundControlStream == null)
123123
{
124-
var reader = MultiplexedConnectionContext.ToClientAcceptQueue.Reader;
125-
while (await reader.WaitToReadAsync())
124+
return await CreateNewInboundControlStream();
125+
}
126+
127+
return null;
128+
}
129+
130+
internal async ValueTask<Http3ControlStream> CreateNewInboundControlStream()
131+
{
132+
var reader = MultiplexedConnectionContext.ToClientAcceptQueue.Reader;
133+
while (await reader.WaitToReadAsync())
134+
{
135+
while (reader.TryRead(out var stream))
126136
{
127-
while (reader.TryRead(out var stream))
128-
{
129-
_inboundControlStream = stream;
130-
var streamId = await stream.TryReadStreamIdAsync();
131-
Debug.Assert(streamId == 0, "StreamId sent that was non-zero, which isn't handled by tests");
132-
return _inboundControlStream;
133-
}
137+
_inboundControlStream = stream;
138+
var streamId = await stream.TryReadStreamIdAsync();
139+
Debug.Assert(streamId == 0, "StreamId sent that was non-zero, which isn't handled by tests");
140+
return _inboundControlStream;
134141
}
135-
}
136-
137-
return null;
142+
}
143+
144+
throw new InvalidOperationException("Should never reach here.");
138145
}
139146

140147
internal async Task WaitForConnectionErrorAsync<TException>(bool ignoreNonGoAwayFrames, long expectedLastStreamId, Http3ErrorCode expectedErrorCode, params string[] expectedErrorMessage)
@@ -152,7 +159,7 @@ internal async Task WaitForConnectionErrorAsync<TException>(bool ignoreNonGoAway
152159

153160
VerifyGoAway(frame, expectedLastStreamId);
154161

155-
Assert.Equal((long)expectedErrorCode, MultiplexedConnectionContext.Error);
162+
Assert.Equal((Http3ErrorCode)expectedErrorCode, (Http3ErrorCode)MultiplexedConnectionContext.Error);
156163

157164
if (expectedErrorMessage?.Length > 0)
158165
{
@@ -180,6 +187,8 @@ protected async Task InitializeConnectionAsync(RequestDelegate application)
180187
// Skip all heartbeat and lifetime notification feature registrations.
181188
_connectionTask = Connection.ProcessStreamsAsync(new DummyApplication(application));
182189

190+
await GetInboundControlStream();
191+
183192
await Task.CompletedTask;
184193
}
185194

@@ -189,8 +198,6 @@ internal async ValueTask<Http3RequestStream> InitializeConnectionAndStreamsAsync
189198

190199
OutboundControlStream = await CreateControlStream();
191200

192-
await GetInboundControlStream();
193-
194201
return await CreateRequestStream();
195202
}
196203

0 commit comments

Comments
 (0)