public async Task ConnectionRequestClose_OSSupport_SendsGoAway() { using var server = Utilities.CreateDynamicHttpsServer(out var address, httpContext => { httpContext.Connection.RequestClose(); return(Task.FromResult(0)); }); await new HostBuilder() .UseHttp2Cat(address, async h2Connection => { await h2Connection.InitializeConnectionAsync(); h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1."); await h2Connection.StartStreamAsync(1, Http2Utilities.BrowserRequestHeaders, endStream: true); var goAwayFrame = await h2Connection.ReceiveFrameAsync(); h2Connection.VerifyGoAway(goAwayFrame, int.MaxValue, Http2ErrorCode.NO_ERROR); await h2Connection.ReceiveHeadersAsync(1, decodedHeaders => { // HTTP/2 filters out the connection header Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection)); Assert.Equal("200", decodedHeaders[HeaderNames.Status]); }); var dataFrame = await h2Connection.ReceiveFrameAsync(); Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0); // Http.Sys doesn't send a final GoAway unless we ignore the first one and send 200 additional streams. h2Connection.Logger.LogInformation("Connection stopped."); }) .Build().RunAsync(); }
public async Task Reset_BeforeRequestBody_Resets() { var deploymentParameters = GetHttpsDeploymentParameters(); var deploymentResult = await DeployAsync(deploymentParameters); await new HostBuilder() .UseHttp2Cat(deploymentResult.ApplicationBaseUri, async h2Connection => { await h2Connection.InitializeConnectionAsync(); h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1."); await h2Connection.StartStreamAsync(1, GetPostHeaders("/Reset_BeforeRequestBody_Resets"), endStream: false); // Any app errors? //Assert.Equal(0, await appResult.Task.DefaultTimeout()); var resetFrame = await h2Connection.ReceiveFrameAsync(); Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, expectedErrorCode: (Http2ErrorCode)1111); h2Connection.Logger.LogInformation("Connection stopped."); }) .Build().RunAsync(); }
public async Task AppException_AfterHeaders_ResetInternalError() { await new HostBuilder() .UseHttp2Cat(Fixture.Client.BaseAddress.AbsoluteUri, async h2Connection => { await h2Connection.InitializeConnectionAsync(); h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1."); await h2Connection.StartStreamAsync(1, GetHeaders("/AppException_AfterHeaders_ResetInternalError"), endStream: true); await h2Connection.ReceiveHeadersAsync(1, decodedHeaders => { Assert.Equal("200", decodedHeaders[HeaderNames.Status]); }); var frame = await h2Connection.ReceiveFrameAsync(); Http2Utilities.VerifyResetFrame(frame, expectedStreamId: 1, Http2ErrorCode.INTERNAL_ERROR); h2Connection.Logger.LogInformation("Connection stopped."); }) .Build().RunAsync(); }
public async Task Http2_PostRequestWithoutData_LengthRequired(string method) { var deploymentParameters = GetHttpsDeploymentParameters(); var deploymentResult = await DeployAsync(deploymentParameters); await new HostBuilder() .UseHttp2Cat(deploymentResult.ApplicationBaseUri, async h2Connection => { await h2Connection.InitializeConnectionAsync(); h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1."); var headers = new[] { new KeyValuePair <string, string>(HeaderNames.Method, method), new KeyValuePair <string, string>(HeaderNames.Path, "/"), new KeyValuePair <string, string>(HeaderNames.Scheme, "https"), new KeyValuePair <string, string>(HeaderNames.Authority, "localhost:443"), }; await h2Connection.StartStreamAsync(1, headers, endStream: true); await h2Connection.ReceiveHeadersAsync(1, decodedHeaders => { Assert.Equal("411", decodedHeaders[HeaderNames.Status]); }); var dataFrame = await h2Connection.ReceiveFrameAsync(); Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: false, length: 344); dataFrame = await h2Connection.ReceiveFrameAsync(); Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0); h2Connection.Logger.LogInformation("Connection stopped."); }) .Build().RunAsync(); }
public async Task AppException_BeforeResponseHeaders_500() { using var server = Utilities.CreateDynamicHttpsServer(out var address, httpContext => { throw new Exception("Application exception"); }); await new HostBuilder() .UseHttp2Cat(address, async h2Connection => { await h2Connection.InitializeConnectionAsync(); h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1."); await h2Connection.StartStreamAsync(1, Http2Utilities.BrowserRequestHeaders, endStream: true); await h2Connection.ReceiveHeadersAsync(1, decodedHeaders => { Assert.Equal("500", decodedHeaders[HeaderNames.Status]); }); var dataFrame = await h2Connection.ReceiveFrameAsync(); if (Environment.OSVersion.Version >= Win10_Regressed_DataFrame) { // TODO: Remove when the regression is fixed. // https://github.com/dotnet/aspnetcore/issues/23164#issuecomment-652646163 Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: false, length: 0); dataFrame = await h2Connection.ReceiveFrameAsync(); } Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0); h2Connection.Logger.LogInformation("Connection stopped."); }) .Build().RunAsync(); }
private async Task RunAsync() { try { var address = BindingAddress.Parse(Options.Url); if (!IPAddress.TryParse(address.Host, out var ip)) { ip = Dns.GetHostEntry(address.Host).AddressList.First(); } var endpoint = new IPEndPoint(ip, address.Port); _logger.LogInformation($"Connecting to '{endpoint}'."); await using var context = await _connectionFactory.ConnectAsync(endpoint); _logger.LogInformation($"Connected to '{endpoint}'."); var originalTransport = context.Transport; IAsyncDisposable sslState = null; if (address.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase)) { _logger.LogInformation("Starting TLS handshake."); var memoryPool = context.Features.Get <IMemoryPoolFeature>()?.MemoryPool; var inputPipeOptions = new StreamPipeReaderOptions(memoryPool, memoryPool.GetMinimumSegmentSize(), memoryPool.GetMinimumAllocSize(), leaveOpen: true); var outputPipeOptions = new StreamPipeWriterOptions(pool: memoryPool, leaveOpen: true); var sslDuplexPipe = new SslDuplexPipe(context.Transport, inputPipeOptions, outputPipeOptions); var sslStream = sslDuplexPipe.Stream; sslState = sslDuplexPipe; context.Transport = sslDuplexPipe; await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions { TargetHost = address.Host, RemoteCertificateValidationCallback = (_, __, ___, ____) => true, ApplicationProtocols = new List <SslApplicationProtocol> { SslApplicationProtocol.Http2 }, EnabledSslProtocols = SslProtocols.Tls12, }, CancellationToken.None); _logger.LogInformation($"TLS handshake completed successfully."); } var http2Utilities = new Http2Utilities(context, _logger, _stopTokenSource.Token); try { await Options.Scenaro(http2Utilities); } catch (Exception ex) { _logger.LogError(ex, "App error"); throw; } finally { // Unwind Https for shutdown. This must happen before the context goes out of scope or else DisposeAsync will never complete context.Transport = originalTransport; if (sslState != null) { await sslState.DisposeAsync(); } } } finally { HostApplicationLifetime.StopApplication(); } }
public async Task Http2_RequestWithDataAndNoContentLength_Success(string method) { var deploymentParameters = GetHttpsDeploymentParameters(); var deploymentResult = await DeployAsync(deploymentParameters); await new HostBuilder() .UseHttp2Cat(deploymentResult.ApplicationBaseUri, async h2Connection => { await h2Connection.InitializeConnectionAsync(); h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1."); var headers = new[] { new KeyValuePair <string, string>(HeaderNames.Method, method), new KeyValuePair <string, string>(HeaderNames.Path, "/Http2_RequestWithDataAndNoContentLength_Success"), new KeyValuePair <string, string>(HeaderNames.Scheme, "https"), new KeyValuePair <string, string>(HeaderNames.Authority, "localhost:443"), }; await h2Connection.StartStreamAsync(1, headers, endStream: false); await h2Connection.SendDataAsync(1, Encoding.UTF8.GetBytes("Hello World"), endStream: true); // Http.Sys no longer sends a window update here on later versions. if (Environment.OSVersion.Version < new Version(10, 0, 19041, 0)) { var windowUpdate = await h2Connection.ReceiveFrameAsync(); Assert.Equal(Http2FrameType.WINDOW_UPDATE, windowUpdate.Type); } await h2Connection.ReceiveHeadersAsync(1, decodedHeaders => { Assert.Equal("200", decodedHeaders[HeaderNames.Status]); }); var dataFrame = await h2Connection.ReceiveFrameAsync(); Assert.Equal(Http2FrameType.DATA, dataFrame.Type); Assert.Equal(1, dataFrame.StreamId); // Some versions send an empty data frame first. if (dataFrame.PayloadLength == 0) { Assert.False(dataFrame.DataEndStream); dataFrame = await h2Connection.ReceiveFrameAsync(); Assert.Equal(Http2FrameType.DATA, dataFrame.Type); Assert.Equal(1, dataFrame.StreamId); } Assert.Equal(11, dataFrame.PayloadLength); Assert.Equal("Hello World", Encoding.UTF8.GetString(dataFrame.Payload.Span)); if (!dataFrame.DataEndStream) { dataFrame = await h2Connection.ReceiveFrameAsync(); Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0); } h2Connection.Logger.LogInformation("Connection stopped."); }) .Build().RunAsync(); }
public async Task ConnectionClose_AdditionalRequests_ReceivesSecondGoAway() { using var server = Utilities.CreateDynamicHttpsServer(out var address, httpContext => { httpContext.Response.Headers.Connection = "close"; return(Task.FromResult(0)); }); await new HostBuilder() .UseHttp2Cat(address, async h2Connection => { await h2Connection.InitializeConnectionAsync(); h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1."); var streamId = 1; await h2Connection.StartStreamAsync(streamId, Http2Utilities.BrowserRequestHeaders, endStream: true); var goAwayFrame = await h2Connection.ReceiveFrameAsync(); Http2Utilities.VerifyGoAway(goAwayFrame, int.MaxValue, Http2ErrorCode.NO_ERROR); await h2Connection.ReceiveHeadersAsync(streamId, decodedHeaders => { // HTTP/2 filters out the connection header Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection)); Assert.Equal("200", decodedHeaders[InternalHeaderNames.Status]); }); var dataFrame = await h2Connection.ReceiveFrameAsync(); Http2Utilities.VerifyDataFrame(dataFrame, streamId, endOfStream: true, length: 0); // Http.Sys doesn't send a final GoAway unless we ignore the first one and send 200 additional streams. for (var i = 1; i < 200; i++) { streamId = 1 + (i * 2); // Odds. await h2Connection.StartStreamAsync(streamId, Http2Utilities.BrowserRequestHeaders, endStream: true); await h2Connection.ReceiveHeadersAsync(streamId, decodedHeaders => { // HTTP/2 filters out the connection header Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection)); Assert.Equal("200", decodedHeaders[InternalHeaderNames.Status]); }); dataFrame = await h2Connection.ReceiveFrameAsync(); Http2Utilities.VerifyDataFrame(dataFrame, streamId, endOfStream: true, length: 0); } streamId = 1 + (200 * 2); // Odds. await h2Connection.StartStreamAsync(streamId, Http2Utilities.BrowserRequestHeaders, endStream: true); // Final GoAway goAwayFrame = await h2Connection.ReceiveFrameAsync(); Http2Utilities.VerifyGoAway(goAwayFrame, streamId, Http2ErrorCode.NO_ERROR); // Normal response await h2Connection.ReceiveHeadersAsync(streamId, decodedHeaders => { // HTTP/2 filters out the connection header Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection)); Assert.Equal("200", decodedHeaders[InternalHeaderNames.Status]); }); dataFrame = await h2Connection.ReceiveFrameAsync(); Http2Utilities.VerifyDataFrame(dataFrame, streamId, endOfStream: true, length: 0); h2Connection.Logger.LogInformation("Connection stopped."); }) .Build().RunAsync(); }
public async Task Reset_CompleteAsyncDurringRequestBody_Resets() { var appResult = new TaskCompletionSource <int>(TaskCreationOptions.RunContinuationsAsynchronously); using var server = Utilities.CreateDynamicHttpsServer(out var address, async httpContext => { try { Assert.Equal("HTTP/2", httpContext.Request.Protocol); var feature = httpContext.Features.Get <IHttpResetFeature>(); Assert.NotNull(feature); var read = await httpContext.Request.Body.ReadAsync(new byte[10], 0, 10); Assert.Equal(10, read); var readTask = httpContext.Request.Body.ReadAsync(new byte[10], 0, 10); await httpContext.Response.CompleteAsync(); feature.Reset((int)Http2ErrorCode.NO_ERROR); // GRPC does this await Assert.ThrowsAsync <IOException>(() => readTask); appResult.SetResult(0); } catch (Exception ex) { appResult.SetException(ex); } }); await new HostBuilder() .UseHttp2Cat(address, async h2Connection => { await h2Connection.InitializeConnectionAsync(); h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1."); await h2Connection.StartStreamAsync(1, Http2Utilities.PostRequestHeaders, endStream: false); await h2Connection.SendDataAsync(1, new byte[10], endStream: false); // Any app errors? Assert.Equal(0, await appResult.Task.DefaultTimeout()); await h2Connection.ReceiveHeadersAsync(1, decodedHeaders => { Assert.Equal("200", decodedHeaders[HeaderNames.Status]); }); var dataFrame = await h2Connection.ReceiveFrameAsync(); if (Environment.OSVersion.Version >= Win10_Regressed_DataFrame) { // TODO: Remove when the regression is fixed. // https://github.com/dotnet/aspnetcore/issues/23164#issuecomment-652646163 Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: false, length: 0); dataFrame = await h2Connection.ReceiveFrameAsync(); } Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0); var resetFrame = await h2Connection.ReceiveFrameAsync(); Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, expectedErrorCode: Http2ErrorCode.NO_ERROR); h2Connection.Logger.LogInformation("Connection stopped."); }) .Build().RunAsync(); }