Skip to content

Commit f4d40e7

Browse files
authored
[release/6.0-staging] [HTTP/2] Fix handling of effectively empty DATA frame (#99502) (#99678)
* [Http/2] Fix handling of effectively empty DATA frame (#99502) * Fix handling effectively empty DATA frame * Added test * Added missing method for the test
1 parent c22b075 commit f4d40e7

File tree

2 files changed

+81
-3
lines changed

2 files changed

+81
-3
lines changed

src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -746,12 +746,14 @@ private void ProcessDataFrame(FrameHeader frameHeader)
746746
// Just ignore the frame in this case.
747747

748748
ReadOnlySpan<byte> frameData = GetFrameData(_incomingBuffer.ActiveSpan.Slice(0, frameHeader.PayloadLength), hasPad: frameHeader.PaddedFlag, hasPriority: false);
749-
750749
if (http2Stream != null)
751750
{
752751
bool endStream = frameHeader.EndStreamFlag;
753752

754-
http2Stream.OnResponseData(frameData, endStream);
753+
if (frameData.Length > 0 || endStream)
754+
{
755+
http2Stream.OnResponseData(frameData, endStream);
756+
}
755757

756758
if (!endStream && frameData.Length > 0)
757759
{

src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,82 @@ public async Task Http2_ZeroLengthResponseBody_Success()
177177
}
178178
}
179179

180+
[Fact]
181+
public async Task Http2_DataFrameOnlyPadding_Success()
182+
{
183+
using (Http2LoopbackServer server = Http2LoopbackServer.CreateServer())
184+
using (HttpClient client = CreateHttpClient())
185+
{
186+
Task<HttpResponseMessage> sendTask = client.GetAsync(server.Address, HttpCompletionOption.ResponseHeadersRead);
187+
188+
Http2LoopbackConnection connection = await server.EstablishConnectionAsync();
189+
190+
int streamId = await connection.ReadRequestHeaderAsync();
191+
192+
await connection.SendDefaultResponseHeadersAsync(streamId);
193+
194+
// Send zero-length DATA frame with padding
195+
byte paddingLength = byte.MaxValue;
196+
int dataLength = 1024;
197+
DataFrame frame = new DataFrame(new byte[0], FrameFlags.Padded, paddingLength, streamId);
198+
await connection.WriteFrameAsync(frame);
199+
200+
HttpResponseMessage response = await sendTask;
201+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
202+
203+
using var responseStream = response.Content.ReadAsStream();
204+
205+
// The read must pend because we havent received any data yet.
206+
var buffer = new byte[dataLength];
207+
var readTask = ReadAtLeastAsync(responseStream, buffer, dataLength);
208+
Assert.False(readTask.IsCompleted);
209+
210+
// Send DATA frame with padding
211+
frame = new DataFrame(new byte[dataLength], FrameFlags.Padded, paddingLength, streamId);
212+
await connection.WriteFrameAsync(frame);
213+
214+
Assert.Equal(dataLength, await readTask);
215+
216+
// Send zero-length, end-stream DATA frame with padding
217+
frame = new DataFrame(new byte[0], FrameFlags.Padded | FrameFlags.EndStream, paddingLength, streamId);
218+
await connection.WriteFrameAsync(frame);
219+
220+
Assert.Equal(0, await responseStream.ReadAsync(buffer));
221+
}
222+
}
223+
224+
private static async ValueTask<int> ReadAtLeastAsync(Stream stream, Memory<byte> buffer, int minimumBytes, bool throwOnEndOfStream = true, CancellationToken cancellationToken = default)
225+
{
226+
if (minimumBytes < 0)
227+
{
228+
throw new ArgumentOutOfRangeException(nameof(minimumBytes));
229+
}
230+
if (buffer.Length < minimumBytes)
231+
{
232+
throw new ArgumentOutOfRangeException($"{nameof(buffer)}.{nameof(buffer.Length)}");
233+
}
234+
235+
int totalRead = 0;
236+
while (totalRead < minimumBytes)
237+
{
238+
int read = await stream.ReadAsync(buffer.Slice(totalRead), cancellationToken).ConfigureAwait(false);
239+
if (read == 0)
240+
{
241+
if (throwOnEndOfStream)
242+
{
243+
throw new EndOfStreamException();
244+
}
245+
246+
return totalRead;
247+
}
248+
249+
totalRead += read;
250+
}
251+
252+
return totalRead;
253+
}
254+
255+
180256
[Theory]
181257
[InlineData("Client content", null)]
182258
[InlineData("Client content", "Server content")]
@@ -204,7 +280,7 @@ await Http2LoopbackServer.CreateClientAndServerAsync(async uri =>
204280
Http2LoopbackConnection connection = await server.EstablishConnectionAsync();
205281
Assert.IsNotType<SslStream>(connection.Stream);
206282

207-
HttpRequestData requestData = await connection.ReadRequestDataAsync();
283+
HttpRequestData requestData = await connection.ReadRequestDataAsync();
208284
string requestContent = requestData.Body is null ? (string)null : Encoding.ASCII.GetString(requestData.Body);
209285
Assert.Equal(clientContent, requestContent);
210286
await connection.SendResponseAsync(HttpStatusCode.OK, content: serverContent);

0 commit comments

Comments
 (0)