test fixes for various handlers and some additional tests
authorGeoff Kizer <geoffrek>
Wed, 9 Jan 2019 11:37:45 +0000 (03:37 -0800)
committerGeoff Kizer <geoffrek>
Wed, 6 Feb 2019 22:01:45 +0000 (14:01 -0800)
Commit migrated from https://github.com/dotnet/corefx/commit/d1c85401a2d9d727e4f87dab62277d569cbc09c5

src/libraries/Common/tests/System/Net/Http/Http2Frames.cs
src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs
src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs
src/libraries/System.Net.Http/tests/FunctionalTests/PlatformHandlerTest.cs

index 293df77..2250a12 100644 (file)
@@ -168,7 +168,7 @@ namespace System.Net.Test.Common
 
         public static HeadersFrame ReadFrom(Frame header, ReadOnlySpan<byte> buffer)
         {
-            int idx = Frame.FrameHeaderLength;
+            int idx = 0;
 
             byte padLength = (byte)(header.PaddedFlag ? buffer[idx++] : 0);
             int streamDependency = header.PriorityFlag ? (int)((uint)((buffer[idx++] << 24) | (buffer[idx++] << 16) | (buffer[idx++] << idx++) | buffer[idx++]) & 0x7FFFFFFF) : 0;
index fa63d5b..60969b8 100644 (file)
@@ -225,7 +225,7 @@ namespace System.Net.Http
                     {
                         if (_responseDataAvailable != null)
                         {
-                            _responseDataAvailable.SetResult(true);
+                            _responseDataAvailable.SetException(new IOException(SR.net_http_invalid_response));
                             _responseDataAvailable = null;
                         }
                     }
index 35e230b..8fe1e8d 100644 (file)
@@ -175,18 +175,24 @@ namespace System.Net.Http.Functional.Tests
         }
 
         [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 });
@@ -196,21 +202,80 @@ namespace System.Net.Http.Functional.Tests
         }
 
         [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);
@@ -249,13 +314,19 @@ namespace System.Net.Http.Functional.Tests
         [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();
@@ -275,6 +346,12 @@ namespace System.Net.Http.Functional.Tests
         [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;
 
@@ -284,7 +361,7 @@ namespace System.Net.Http.Functional.Tests
                 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);
@@ -370,10 +447,10 @@ namespace System.Net.Http.Functional.Tests
                 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);
-
             }
         }
 
@@ -401,6 +478,7 @@ namespace System.Net.Http.Functional.Tests
                 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);
@@ -420,6 +498,7 @@ namespace System.Net.Http.Functional.Tests
 
                 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);
@@ -431,12 +510,37 @@ namespace System.Net.Http.Functional.Tests
                 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()
@@ -534,30 +638,10 @@ namespace System.Net.Http.Functional.Tests
                 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;
@@ -682,30 +766,10 @@ namespace System.Net.Http.Functional.Tests
                 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;
index 798290a..24d491b 100644 (file)
@@ -154,8 +154,10 @@ namespace System.Net.Http.Functional.Tests
         protected override bool UseSocketsHttpHandler => false;
     }
 
+#if netcoreapp
     public sealed class PlatformHandlerTest_Http2 : HttpClientHandlerTest_Http2
     {
         protected override bool UseSocketsHttpHandler => false;
     }
+#endif
 }