}
[ConditionalTheory(nameof(SupportsAlpn))]
- [InlineData(SettingId.MaxFrameSize, 16383)]
- [InlineData(SettingId.MaxFrameSize, 162777216)]
- [InlineData(SettingId.InitialWindowSize, 0x80000000)]
- public async Task Http2_ServerSendsInvalidSettingsValue_ProtocolError(SettingId settingId, uint value)
+ [InlineData(SettingId.MaxFrameSize, 16383, true)]
+ [InlineData(SettingId.MaxFrameSize, 162777216, true)]
+ [InlineData(SettingId.InitialWindowSize, 0x80000000, false)]
+ public async Task Http2_ServerSendsInvalidSettingsValue_ProtocolError(SettingId settingId, uint value, bool skipForWinHttp)
{
+ if (IsWinHttpHandler && skipForWinHttp)
+ {
+ // WinHTTP does not treat these as errors, it seems to ignore the invalid setting.
+ return;
+ }
+
HttpClientHandler handler = CreateHttpClientHandler();
handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates;
using (var server = Http2LoopbackServer.CreateServer())
using (var client = new HttpClient(handler))
{
- Task sendTask = client.GetAsync(server.Address);
+ Task<HttpResponseMessage> sendTask = client.GetAsync(server.Address);
// Send invalid initial SETTINGS value
await server.EstablishConnectionAsync(new SettingsEntry { SettingId = settingId, Value = value });
}
[ConditionalFact(nameof(SupportsAlpn))]
- public async Task Http2_StreamResetByServer_RequestFails()
+ public async Task Http2_StreamResetByServerBeforeHeadersSent_RequestFails()
{
+ if (IsWinHttpHandler)
+ {
+ // WinHTTP does not genenerate an exception here.
+ // It seems to ignore a RST_STREAM sent before headers are sent, and continue to wait for HEADERS.
+ return;
+ }
+
HttpClientHandler handler = CreateHttpClientHandler();
handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates;
using (var server = Http2LoopbackServer.CreateServer())
using (var client = new HttpClient(handler))
{
- Task sendTask = client.GetAsync(server.Address);
+ Task<HttpResponseMessage> sendTask = client.GetAsync(server.Address);
await server.EstablishConnectionAsync();
- await server.ReadRequestHeaderAsync();
+ int streamId = await server.ReadRequestHeaderAsync();
- // Send a reset stream frame so that stream 1 moves to a terminal state.
- RstStreamFrame resetStream = new RstStreamFrame(FrameFlags.Padded, 0x1, 1);
+ // Send a reset stream frame so that the stream moves to a terminal state.
+ RstStreamFrame resetStream = new RstStreamFrame(FrameFlags.None, 0x2, streamId);
+ await server.WriteFrameAsync(resetStream);
+
+ await Assert.ThrowsAsync<HttpRequestException>(async () => await sendTask);
+ }
+ }
+
+ [ConditionalFact(nameof(SupportsAlpn))]
+ public async Task Http2_StreamResetByServerAfterHeadersSent_RequestFails()
+ {
+ HttpClientHandler handler = CreateHttpClientHandler();
+ handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates;
+
+ using (var server = Http2LoopbackServer.CreateServer())
+ using (var client = new HttpClient(handler))
+ {
+ Task<HttpResponseMessage> sendTask = client.GetAsync(server.Address);
+
+ await server.EstablishConnectionAsync();
+ int streamId = await server.ReadRequestHeaderAsync();
+
+ // Send response headers
+ await server.SendDefaultResponseHeadersAsync(streamId);
+
+ // Send a reset stream frame so that the stream moves to a terminal state.
+ RstStreamFrame resetStream = new RstStreamFrame(FrameFlags.None, 0x2, streamId);
+ await server.WriteFrameAsync(resetStream);
+
+ await Assert.ThrowsAsync<HttpRequestException>(async () => await sendTask);
+ }
+ }
+
+ [ConditionalFact(nameof(SupportsAlpn))]
+ public async Task Http2_StreamResetByServerAfterPartialBodySent_RequestFails()
+ {
+ HttpClientHandler handler = CreateHttpClientHandler();
+ handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates;
+
+ using (var server = Http2LoopbackServer.CreateServer())
+ using (var client = new HttpClient(handler))
+ {
+ Task<HttpResponseMessage> sendTask = client.GetAsync(server.Address);
+
+ await server.EstablishConnectionAsync();
+ int streamId = await server.ReadRequestHeaderAsync();
+
+ // Send response headers and partial response body
+ await server.SendDefaultResponseHeadersAsync(streamId);
+ DataFrame dataFrame = new DataFrame(new byte[10], FrameFlags.None, 0, streamId);
+ await server.WriteFrameAsync(dataFrame);
+
+ // Send a reset stream frame so that the stream moves to a terminal state.
+ RstStreamFrame resetStream = new RstStreamFrame(FrameFlags.None, 0x2, streamId);
await server.WriteFrameAsync(resetStream);
await Assert.ThrowsAsync<HttpRequestException>(async () => await sendTask);
[ConditionalFact(nameof(SupportsAlpn))]
public async Task DataFrame_IdleStream_ConnectionError()
{
+ if (IsWinHttpHandler)
+ {
+ // WinHTTP does not treat this as an error, it seems to ignore the invalid frame.
+ return;
+ }
+
HttpClientHandler handler = CreateHttpClientHandler();
handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates;
using (var server = Http2LoopbackServer.CreateServer())
using (var client = new HttpClient(handler))
{
- Task sendTask = client.GetAsync(server.Address);
+ Task<HttpResponseMessage> sendTask = client.GetAsync(server.Address);
await server.EstablishConnectionAsync();
await server.ReadRequestHeaderAsync();
[ConditionalFact(nameof(SupportsAlpn))]
public async Task HeadersFrame_IdleStream_ConnectionError()
{
+ if (IsWinHttpHandler)
+ {
+ // WinHTTP does not treat this as an error, it seems to ignore the HEADERS frame.
+ return;
+ }
+
HttpClientHandler handler = CreateHttpClientHandler();
handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates;
Task sendTask = client.GetAsync(server.Address);
await server.EstablishConnectionAsync();
- await server.ReadRequestHeaderAsync();
+ int streamId = await server.ReadRequestHeaderAsync();
// Send a headers frame on stream 5, which is in the idle state.
await server.SendDefaultResponseHeadersAsync(5);
DataFrame invalidFrame = new DataFrame(new byte[10], FrameFlags.None, 0, streamId);
await server.WriteFrameAsync(invalidFrame);
+// TODO: WinHttpHandler?
// The server should close the connection as this is a fatal connection level error.
Exception ex = await Assert.ThrowsAsync<Exception>(async () => await server.ReadFrameAsync(TimeSpan.FromSeconds(30)));
Assert.Equal("Connection stream closed while attempting to read frame header.", ex.Message);
-
}
}
DataFrame invalidFrame = new DataFrame(new byte[10], FrameFlags.None, 0, streamId);
await server.WriteFrameAsync(invalidFrame);
+// TODO: WinHttpHandler?
// The server should close the connection as this is a fatal connection level error.
Exception ex = await Assert.ThrowsAsync<Exception>(async () => await server.ReadFrameAsync(TimeSpan.FromSeconds(30)));
Assert.Equal("Connection stream closed while attempting to read frame header.", ex.Message);
await server.EstablishConnectionAsync();
int streamId = await server.ReadRequestHeaderAsync();
+ await server.SendDefaultResponseHeadersAsync(streamId);
// Send a reset stream frame so that stream 1 moves to a terminal state.
RstStreamFrame resetStream = new RstStreamFrame(FrameFlags.None, 0x1, streamId);
DataFrame invalidFrame = new DataFrame(new byte[10], FrameFlags.None, 0, streamId);
await server.WriteFrameAsync(invalidFrame);
+// TODO: WinHttpHandler?
// The server should close the connection as this is a fatal connection level error.
Exception ex = await Assert.ThrowsAsync<Exception>(async () => await server.ReadFrameAsync(TimeSpan.FromSeconds(30)));
Assert.Equal("Connection stream closed while attempting to read frame header.", ex.Message);
}
}
+ private static async Task<int> ReadToEndOfStream(Http2LoopbackServer server, int streamId)
+ {
+ int bytesReceived = 0;
+ while (true)
+ {
+ Frame frame = await server.ReadFrameAsync(TimeSpan.FromSeconds(30));
+
+ Assert.Equal(streamId, frame.StreamId);
+ Assert.Equal(FrameType.Data, frame.Type);
+
+ bytesReceived += frame.Length;
+
+ if (frame.Flags == FrameFlags.EndStream)
+ {
+ break;
+ }
+
+ Assert.Equal(FrameFlags.None, frame.Flags);
+ Assert.True(frame.Length > 0);
+ }
+
+ return bytesReceived;
+ }
+
[OuterLoop("Uses Task.Delay")]
[ConditionalFact(nameof(SupportsAlpn))]
public async Task Http2_FlowControl_ClientDoesNotExceedWindows()
bytesReceived += frame.Length;
// Read to end of stream
- while (true)
- {
- frame = await server.ReadFrameAsync(TimeSpan.FromSeconds(30));
- if (frame.EndStreamFlag)
- {
- break;
- }
-
- Assert.Equal(streamId, frame.StreamId);
- Assert.Equal(FrameType.Data, frame.Type);
- Assert.Equal(FrameFlags.None, frame.Flags);
- Assert.True(frame.Length > 0);
-
- bytesReceived += frame.Length;
- }
+ bytesReceived += await ReadToEndOfStream(server, streamId);
Assert.Equal(ContentSize, bytesReceived);
- // Verify EndStream frame
- Assert.Equal(streamId, frame.StreamId);
- Assert.Equal(FrameType.Data, frame.Type);
- Assert.Equal(FrameFlags.EndStream, frame.Flags);
- Assert.True(frame.Length == 0);
-
await server.SendDefaultResponseAsync(streamId);
HttpResponseMessage response = await clientTask;
bytesReceived += frame.Length;
// Read to end of stream
- while (true)
- {
- frame = await server.ReadFrameAsync(TimeSpan.FromSeconds(30));
- if (frame.EndStreamFlag)
- {
- break;
- }
-
- Assert.Equal(streamId, frame.StreamId);
- Assert.Equal(FrameType.Data, frame.Type);
- Assert.Equal(FrameFlags.None, frame.Flags);
- Assert.True(frame.Length > 0);
-
- bytesReceived += frame.Length;
- }
+ bytesReceived += await ReadToEndOfStream(server, streamId);
Assert.Equal(ContentSize, bytesReceived);
- // Verify EndStream frame
- Assert.Equal(streamId, frame.StreamId);
- Assert.Equal(FrameType.Data, frame.Type);
- Assert.Equal(FrameFlags.EndStream, frame.Flags);
- Assert.True(frame.Length == 0);
-
await server.SendDefaultResponseAsync(streamId);
HttpResponseMessage response = await clientTask;