Skip to content

Commit

Permalink
[release/6.0-staging] [HTTP/2] Fix handling of effectively empty DATA…
Browse files Browse the repository at this point in the history
… 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
  • Loading branch information
ManickaP committed Mar 26, 2024
1 parent c22b075 commit f4d40e7
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -746,12 +746,14 @@ private void ProcessDataFrame(FrameHeader frameHeader)
// Just ignore the frame in this case.

ReadOnlySpan<byte> frameData = GetFrameData(_incomingBuffer.ActiveSpan.Slice(0, frameHeader.PayloadLength), hasPad: frameHeader.PaddedFlag, hasPriority: false);

if (http2Stream != null)
{
bool endStream = frameHeader.EndStreamFlag;

http2Stream.OnResponseData(frameData, endStream);
if (frameData.Length > 0 || endStream)
{
http2Stream.OnResponseData(frameData, endStream);
}

if (!endStream && frameData.Length > 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,82 @@ public async Task Http2_ZeroLengthResponseBody_Success()
}
}

[Fact]
public async Task Http2_DataFrameOnlyPadding_Success()
{
using (Http2LoopbackServer server = Http2LoopbackServer.CreateServer())
using (HttpClient client = CreateHttpClient())
{
Task<HttpResponseMessage> sendTask = client.GetAsync(server.Address, HttpCompletionOption.ResponseHeadersRead);

Http2LoopbackConnection connection = await server.EstablishConnectionAsync();

int streamId = await connection.ReadRequestHeaderAsync();

await connection.SendDefaultResponseHeadersAsync(streamId);

// Send zero-length DATA frame with padding
byte paddingLength = byte.MaxValue;
int dataLength = 1024;
DataFrame frame = new DataFrame(new byte[0], FrameFlags.Padded, paddingLength, streamId);
await connection.WriteFrameAsync(frame);

HttpResponseMessage response = await sendTask;
Assert.Equal(HttpStatusCode.OK, response.StatusCode);

using var responseStream = response.Content.ReadAsStream();

// The read must pend because we havent received any data yet.
var buffer = new byte[dataLength];
var readTask = ReadAtLeastAsync(responseStream, buffer, dataLength);
Assert.False(readTask.IsCompleted);

// Send DATA frame with padding
frame = new DataFrame(new byte[dataLength], FrameFlags.Padded, paddingLength, streamId);
await connection.WriteFrameAsync(frame);

Assert.Equal(dataLength, await readTask);

// Send zero-length, end-stream DATA frame with padding
frame = new DataFrame(new byte[0], FrameFlags.Padded | FrameFlags.EndStream, paddingLength, streamId);
await connection.WriteFrameAsync(frame);

Assert.Equal(0, await responseStream.ReadAsync(buffer));
}
}

private static async ValueTask<int> ReadAtLeastAsync(Stream stream, Memory<byte> buffer, int minimumBytes, bool throwOnEndOfStream = true, CancellationToken cancellationToken = default)
{
if (minimumBytes < 0)
{
throw new ArgumentOutOfRangeException(nameof(minimumBytes));
}
if (buffer.Length < minimumBytes)
{
throw new ArgumentOutOfRangeException($"{nameof(buffer)}.{nameof(buffer.Length)}");
}

int totalRead = 0;
while (totalRead < minimumBytes)
{
int read = await stream.ReadAsync(buffer.Slice(totalRead), cancellationToken).ConfigureAwait(false);
if (read == 0)
{
if (throwOnEndOfStream)
{
throw new EndOfStreamException();
}

return totalRead;
}

totalRead += read;
}

return totalRead;
}


[Theory]
[InlineData("Client content", null)]
[InlineData("Client content", "Server content")]
Expand Down Expand Up @@ -204,7 +280,7 @@ await Http2LoopbackServer.CreateClientAndServerAsync(async uri =>
Http2LoopbackConnection connection = await server.EstablishConnectionAsync();
Assert.IsNotType<SslStream>(connection.Stream);
HttpRequestData requestData = await connection.ReadRequestDataAsync();
HttpRequestData requestData = await connection.ReadRequestDataAsync();
string requestContent = requestData.Body is null ? (string)null : Encoding.ASCII.GetString(requestData.Body);
Assert.Equal(clientContent, requestContent);
await connection.SendResponseAsync(HttpStatusCode.OK, content: serverContent);
Expand Down

0 comments on commit f4d40e7

Please sign in to comment.