protected static HttpClientHandler CreateHttpClientHandler(bool useSocketsHttpHandler, bool useHttp2LoopbackServer = false) { HttpClientHandler handler; if (PlatformDetection.IsUap || PlatformDetection.IsFullFramework || useSocketsHttpHandler) { handler = new HttpClientHandler(); } else { // Create platform specific handler. ConstructorInfo ctor = typeof(HttpClientHandler).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(bool) }, null); Debug.Assert(ctor != null, "Couldn't find test constructor on HttpClientHandler"); handler = (HttpClientHandler)ctor.Invoke(new object[] { useSocketsHttpHandler }); Debug.Assert(useSocketsHttpHandler == IsSocketsHttpHandler(handler), "Unexpected handler."); } TestHelper.EnsureHttp2Feature(handler); if (useHttp2LoopbackServer) { handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; } return(handler); }
public async Task Http2_InitialSettings_SentAndAcked() { HttpClientHandler handler = CreateHttpClientHandler(); handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; TestHelper.EnsureHttp2Feature(handler); using (var server = Http2LoopbackServer.CreateServer()) using (var client = new HttpClient(handler)) { Task sendTask = client.GetAsync(server.Address); await server.AcceptConnectionAsync(); // Receive the initial client settings frame. Frame receivedFrame = await server.ReadFrameAsync(TimeSpan.FromSeconds(30)); Assert.Equal(FrameType.Settings, receivedFrame.Type); // Send the initial server settings frame. Frame emptySettings = new Frame(0, FrameType.Settings, FrameFlags.None, 0); await server.WriteFrameAsync(emptySettings).ConfigureAwait(false); // Receive the server settings frame ACK. receivedFrame = await server.ReadFrameAsync(TimeSpan.FromSeconds(30)); Assert.Equal(FrameType.Settings, receivedFrame.Type); Assert.True(receivedFrame.AckFlag); } }
public async Task Http2_StreamResetByServer_RequestFails() { HttpClientHandler handler = CreateHttpClientHandler(); handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; TestHelper.EnsureHttp2Feature(handler); using (var server = Http2LoopbackServer.CreateServer()) using (var client = new HttpClient(handler)) { Task sendTask = client.GetAsync(server.Address); await server.AcceptConnectionAsync(); await server.SendConnectionPrefaceAsync(); // Receive the request header frame. Frame receivedFrame = await server.ReadFrameAsync(TimeSpan.FromSeconds(30)); // Send a reset stream frame so that stream 1 moves to a terminal state. RstStreamFrame resetStream = new RstStreamFrame(FrameFlags.Padded, 0x1, 1); await server.WriteFrameAsync(resetStream); // This currently throws an IOException. await Assert.ThrowsAsync <HttpRequestException>(async() => await sendTask); } }
public async Task ClosedStream_FrameReceived_ResetsStream() { HttpClientHandler handler = CreateHttpClientHandler(); handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; TestHelper.EnsureHttp2Feature(handler); using (var server = Http2LoopbackServer.CreateServer()) using (var client = new HttpClient(handler)) { Task sendTask = client.GetAsync(server.Address); await server.AcceptConnectionAsync(); await server.SendConnectionPrefaceAsync(); // Receive the request header frame. Frame receivedFrame = await server.ReadFrameAsync(TimeSpan.FromSeconds(30)); // Send a reset stream frame so that stream 1 moves to a terminal state. RstStreamFrame resetStream = new RstStreamFrame(FrameFlags.Padded, 0x1, 1); await server.WriteFrameAsync(resetStream); // Send a frame on the now-closed stream. DataFrame invalidFrame = new DataFrame(new byte[10], FrameFlags.Padded, 10, 1); await server.WriteFrameAsync(invalidFrame); // Receive a RST_STREAM frame. receivedFrame = await server.ReadFrameAsync(TimeSpan.FromSeconds(30)); Assert.Equal(FrameType.RstStream, receivedFrame.Type); await Assert.ThrowsAsync <HttpRequestException>(async() => await sendTask); } }
public async Task DataFrame_TooLong_ConnectionError() { HttpClientHandler handler = CreateHttpClientHandler(); handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; TestHelper.EnsureHttp2Feature(handler); using (var server = Http2LoopbackServer.CreateServer()) using (var client = new HttpClient(handler)) { Task sendTask = client.GetAsync(server.Address); await server.AcceptConnectionAsync(); await server.SendConnectionPrefaceAsync(); // Receive the request header frame. Frame receivedFrame = await server.ReadFrameAsync(TimeSpan.FromSeconds(30)); // Send a malformed frame. DataFrame invalidFrame = new DataFrame(new byte[Frame.MaxFrameLength + 1], FrameFlags.None, 0, 0); await server.WriteFrameAsync(invalidFrame); // As this is a connection level error, the client should see the request fail. await Assert.ThrowsAsync <HttpRequestException>(async() => await sendTask); // The server should receive a GOAWAY frame. receivedFrame = await server.ReadFrameAsync(TimeSpan.FromSeconds(30)); Assert.Equal(FrameType.GoAway, receivedFrame.Type); } }
public async Task Http2_ClientPreface_Sent() { HttpClientHandler handler = CreateHttpClientHandler(); handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; TestHelper.EnsureHttp2Feature(handler); using (var server = Http2LoopbackServer.CreateServer()) using (var client = new HttpClient(handler)) { Task sendTask = client.GetAsync(server.Address); string connectionPreface = await server.AcceptConnectionAsync(); Assert.Equal("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", connectionPreface); } }
public async Task Http2_DataSentBeforeServerPreface_ProtocolError() { HttpClientHandler handler = CreateHttpClientHandler(); handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; TestHelper.EnsureHttp2Feature(handler); using (var server = Http2LoopbackServer.CreateServer()) using (var client = new HttpClient(handler)) { Task sendTask = client.GetAsync(server.Address); await server.AcceptConnectionAsync(); // Send a frame despite not having sent the server connection preface. DataFrame invalidFrame = new DataFrame(new byte[10], FrameFlags.Padded, 10, 1); await server.WriteFrameAsync(invalidFrame); // This currently throws an Http2ProtocolException, but that type is not public. await Assert.ThrowsAsync <HttpRequestException>(async() => await sendTask); } }
public async Task Http2_FlowControl_ClientDoesNotExceedWindows() { const int InitialWindowSize = 65535; const int ContentSize = 100_000; HttpClientHandler handler = CreateHttpClientHandler(); handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; TestHelper.EnsureHttp2Feature(handler); var content = new ByteArrayContent(TestHelper.GenerateRandomContent(ContentSize)); using (var server = Http2LoopbackServer.CreateServer()) using (var client = new HttpClient(handler)) { Task <HttpResponseMessage> clientTask = client.PostAsync(server.Address, content); await server.EstablishConnectionAsync(); Frame frame = await server.ReadFrameAsync(TimeSpan.FromSeconds(30)); int streamId = frame.StreamId; Assert.Equal(FrameType.Headers, frame.Type); Assert.Equal(FrameFlags.EndHeaders, frame.Flags); // Receive up to initial window size int bytesReceived = 0; while (bytesReceived < InitialWindowSize) { frame = await server.ReadFrameAsync(TimeSpan.FromSeconds(30)); 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; } Assert.Equal(InitialWindowSize, bytesReceived); // Issue another read. It shouldn't complete yet. Wait a brief period of time to ensure it doesn't complete. Task <Frame> readFrameTask = server.ReadFrameAsync(TimeSpan.FromSeconds(30)); await Task.Delay(500); Assert.False(readFrameTask.IsCompleted); // Increase connection window by one. This should still not complete the read. await server.WriteFrameAsync(new WindowUpdateFrame(1, 0)); await Task.Delay(500); Assert.False(readFrameTask.IsCompleted); // Increase stream window by two. This should complete the read with a single byte. await server.WriteFrameAsync(new WindowUpdateFrame(2, streamId)); frame = await readFrameTask; Assert.Equal(1, frame.Length); bytesReceived++; // Issue another read and ensure it doesn't complete yet. readFrameTask = server.ReadFrameAsync(TimeSpan.FromSeconds(30)); await Task.Delay(500); Assert.False(readFrameTask.IsCompleted); // Increase connection window by two. This should complete the read with a single byte. await server.WriteFrameAsync(new WindowUpdateFrame(2, 0)); frame = await readFrameTask; Assert.Equal(1, frame.Length); bytesReceived++; // Issue another read and ensure it doesn't complete yet. readFrameTask = server.ReadFrameAsync(TimeSpan.FromSeconds(30)); await Task.Delay(500); Assert.False(readFrameTask.IsCompleted); // Increase connection window to allow exactly the remaining request size. This should still not complete the read. await server.WriteFrameAsync(new WindowUpdateFrame(ContentSize - bytesReceived - 1, 0)); await Task.Delay(500); Assert.False(readFrameTask.IsCompleted); // Increase stream window to allow exactly the remaining request size. This should allow the rest of the request to be sent. await server.WriteFrameAsync(new WindowUpdateFrame(ContentSize - bytesReceived, streamId)); frame = await readFrameTask; 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; // 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; } 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; Assert.Equal(HttpStatusCode.OK, response.StatusCode); } }
public async Task Http2_InitialWindowSize_ClientDoesNotExceedWindows() { const int DefaultInitialWindowSize = 65535; const int ContentSize = 100_000; HttpClientHandler handler = CreateHttpClientHandler(); handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; TestHelper.EnsureHttp2Feature(handler); var content = new ByteArrayContent(TestHelper.GenerateRandomContent(ContentSize)); using (var server = Http2LoopbackServer.CreateServer()) using (var client = new HttpClient(handler)) { Task <HttpResponseMessage> clientTask = client.PostAsync(server.Address, content); await server.EstablishConnectionAsync(); // Bump connection window so it won't block the client. await server.WriteFrameAsync(new WindowUpdateFrame(ContentSize - DefaultInitialWindowSize, 0)); Frame frame = await server.ReadFrameAsync(TimeSpan.FromSeconds(30)); int streamId = frame.StreamId; Assert.Equal(FrameType.Headers, frame.Type); Assert.Equal(FrameFlags.EndHeaders, frame.Flags); // Receive up to initial window size int bytesReceived = 0; while (bytesReceived < DefaultInitialWindowSize) { frame = await server.ReadFrameAsync(TimeSpan.FromSeconds(30)); 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; } Assert.Equal(DefaultInitialWindowSize, bytesReceived); // Issue another read. It shouldn't complete yet. Wait a brief period of time to ensure it doesn't complete. Task <Frame> readFrameTask = server.ReadFrameAsync(TimeSpan.FromSeconds(30)); await Task.Delay(500); Assert.False(readFrameTask.IsCompleted); // Change SETTINGS_INITIAL_WINDOW_SIZE to 0. This will make the client's credit go negative. server.ExpectSettingsAck(); await server.WriteFrameAsync(new SettingsFrame(new SettingsEntry { SettingId = SettingId.InitialWindowSize, Value = 0 })); await Task.Delay(500); Assert.False(readFrameTask.IsCompleted); // Increase stream window by one. Client credit will still be negative. await server.WriteFrameAsync(new WindowUpdateFrame(1, streamId)); await Task.Delay(500); Assert.False(readFrameTask.IsCompleted); // Change SETTINGS_INITIAL_WINDOW_SIZE to 1. Client credit will still be negative. server.ExpectSettingsAck(); await server.WriteFrameAsync(new SettingsFrame(new SettingsEntry { SettingId = SettingId.InitialWindowSize, Value = 1 })); await Task.Delay(500); Assert.False(readFrameTask.IsCompleted); // Increase stream window so client credit will be 0. await server.WriteFrameAsync(new WindowUpdateFrame(DefaultInitialWindowSize - 2, streamId)); await Task.Delay(500); Assert.False(readFrameTask.IsCompleted); // Increase stream window by one, so client can now send a single byte. await server.WriteFrameAsync(new WindowUpdateFrame(1, streamId)); frame = await readFrameTask; Assert.Equal(FrameType.Data, frame.Type); Assert.Equal(1, frame.Length); bytesReceived++; // Issue another read and ensure it doesn't complete yet. readFrameTask = server.ReadFrameAsync(TimeSpan.FromSeconds(30)); await Task.Delay(500); Assert.False(readFrameTask.IsCompleted); // Increase SETTINGS_INITIAL_WINDOW_SIZE to 2, so client can now send a single byte. server.ExpectSettingsAck(); await server.WriteFrameAsync(new SettingsFrame(new SettingsEntry { SettingId = SettingId.InitialWindowSize, Value = 2 })); frame = await readFrameTask; Assert.Equal(FrameType.Data, frame.Type); Assert.Equal(1, frame.Length); bytesReceived++; // Issue another read and ensure it doesn't complete yet. readFrameTask = server.ReadFrameAsync(TimeSpan.FromSeconds(30)); await Task.Delay(500); Assert.False(readFrameTask.IsCompleted); // Increase SETTINGS_INITIAL_WINDOW_SIZE to be enough that the client can send the rest of the content. server.ExpectSettingsAck(); await server.WriteFrameAsync(new SettingsFrame(new SettingsEntry { SettingId = SettingId.InitialWindowSize, Value = ContentSize - (DefaultInitialWindowSize - 1) })); frame = await readFrameTask; 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; // Read to end of stream bytesReceived += await ReadToEndOfStream(server, streamId); Assert.Equal(ContentSize, bytesReceived); await server.SendDefaultResponseAsync(streamId); HttpResponseMessage response = await clientTask; Assert.Equal(HttpStatusCode.OK, response.StatusCode); } }
public async Task SendAsync_RemoteServersWithCookies_Success(Uri remoteServer) { // CurlHandler: cookies from container are not sent if a Cookie header is manually added #26983. if (IsCurlHandler) { return; } var expectedVersion = new Version(1, 1); HttpClientHandler handler = CreateHttpClientHandler(); if (remoteServer.Host == Configuration.Http.Http2Host && BackendSupportsAlpn) { // Windows 10 Anniversary release a.k.a Windows 10 Version 1607 added support to native WinHTTP for HTTP/2 protocol support. if (IsWinHttpHandler && !PlatformDetection.IsWindows10Version1607OrGreater) { return; } expectedVersion = new Version(2, 0); TestHelper.EnsureHttp2Feature(handler); } using (HttpClient client = new HttpClient(handler)) { var request = new HttpRequestMessage(HttpMethod.Get, remoteServer) { Version = expectedVersion, }; // Make remote server send SetCookie header. request.Headers.Add("X-SetCookie", "name=value"); request.Headers.Add("X-SetCookie", "name1=value1"); request.Headers.Add("X-SetCookie", "name2=value2"); using (HttpResponseMessage response = await client.SendAsync(request)) { Assert.Equal(expectedVersion, response.Version); Assert.Equal(HttpStatusCode.OK, response.StatusCode); // Verify server sends back SetCookie header. Assert.Contains("Set-Cookie: name=value, name1=value1, name2=value2", response.Headers.ToString()); // Server does not have any cookies yet. Assert.Contains("\"Cookies\": {}", await response.Content.ReadAsStringAsync()); } // Send next request to see if the cookie has been wrote to the header. var newRequest = new HttpRequestMessage(HttpMethod.Get, remoteServer) { Version = expectedVersion, }; // Send additional cookie (along with the ones from CookieContainer). newRequest.Headers.Add("Cookie", "cookie=c1"); using (HttpResponseMessage response = await client.SendAsync(newRequest)) { Assert.Equal(expectedVersion, response.Version); Assert.Equal(HttpStatusCode.OK, response.StatusCode); // Verify server received all cookies. string body = await response.Content.ReadAsStringAsync(); Assert.Contains("\"Cookies\": ", body); Assert.Contains("\"name\": \"value\"", body); Assert.Contains("\"name1\": \"value1\"", body); Assert.Contains("\"name2\": \"value2\"", body); Assert.Contains("\"cookie\": \"c1\"", body); } } }