public async Task KeepAliveTimeout_AfterRequestComplete_ConnectionClosed() { var requestHeaders = new[] { new KeyValuePair <string, string>(InternalHeaderNames.Method, "GET"), new KeyValuePair <string, string>(InternalHeaderNames.Path, "/"), new KeyValuePair <string, string>(InternalHeaderNames.Scheme, "http"), }; var limits = _serviceContext.ServerOptions.Limits; await Http3Api.InitializeConnectionAsync(_noopApplication).DefaultTimeout(); await Http3Api.CreateControlStream(); var controlStream = await Http3Api.GetInboundControlStream().DefaultTimeout(); await controlStream.ExpectSettingsAsync().DefaultTimeout(); var requestStream = await Http3Api.CreateRequestStream(requestHeaders, endStream : true); await requestStream.ExpectHeadersAsync(); await requestStream.ExpectReceiveEndOfStream(); await requestStream.OnDisposedTask.DefaultTimeout(); Http3Api.AdvanceClock(limits.KeepAliveTimeout + Heartbeat.Interval + TimeSpan.FromTicks(1)); await Http3Api.WaitForConnectionStopAsync(4, false, expectedErrorCode : Http3ErrorCode.NoError); }
public async Task ControlStream_ClientToServer_Completes_ConnectionError() { var now = _serviceContext.MockSystemClock.UtcNow; await Http3Api.InitializeConnectionAsync(_noopApplication); var controlStream = await Http3Api.CreateControlStream(id : 0); await controlStream.SendSettingsAsync(new List <Http3PeerSetting>()); await controlStream.EndStreamAsync().DefaultTimeout(); // Wait for control stream to finish processing and exit. await controlStream.OnStreamCompletedTask.DefaultTimeout(); Http3Api.TriggerTick(now); Http3Api.TriggerTick(now + TimeSpan.FromSeconds(1)); await Http3Api.WaitForConnectionErrorAsync <Http3ConnectionErrorException>( ignoreNonGoAwayFrames : true, expectedLastStreamId : 0, expectedErrorCode : Http3ErrorCode.ClosedCriticalStream, matchExpectedErrorMessage : AssertExpectedErrorMessages, expectedErrorMessage : CoreStrings.Http3ErrorControlStreamClosed); }
public async Task SETTINGS_MaxFieldSectionSizeSent_ServerReceivesValue() { await Http3Api.InitializeConnectionAsync(_echoApplication); var inboundControlStream = await Http3Api.GetInboundControlStream(); var incomingSettings = await inboundControlStream.ExpectSettingsAsync(); var defaultLimits = new KestrelServerLimits(); Assert.Collection(incomingSettings, kvp => { Assert.Equal((long)Http3SettingType.MaxFieldSectionSize, kvp.Key); Assert.Equal(defaultLimits.MaxRequestHeadersTotalSize, kvp.Value); }); var outboundcontrolStream = await Http3Api.CreateControlStream(); await outboundcontrolStream.SendSettingsAsync(new List <Http3PeerSetting> { new Http3PeerSetting(Http3SettingType.MaxFieldSectionSize, 100) }); var maxFieldSetting = await Http3Api.ServerReceivedSettingsReader.ReadAsync().DefaultTimeout(); Assert.Equal(Http3SettingType.MaxFieldSectionSize, maxFieldSetting.Key); Assert.Equal(100, maxFieldSetting.Value); }
public async Task ControlStream_HeaderNotReceivedWithinRequestHeadersTimeout_StreamError_PendingStreamsEnabled() { Http3Api._serviceContext.ServerOptions.EnableWebTransportAndH3Datagrams = true; var now = _serviceContext.MockSystemClock.UtcNow; var limits = _serviceContext.ServerOptions.Limits; await Http3Api.InitializeConnectionAsync(_noopApplication).DefaultTimeout(); var controlStream = await Http3Api.GetInboundControlStream().DefaultTimeout(); await controlStream.ExpectSettingsAsync().DefaultTimeout(); var outboundControlStream = await Http3Api.CreateControlStream(id : null); await outboundControlStream.OnUnidentifiedStreamCreatedTask.DefaultTimeout(); var serverInboundControlStream = Http3Api.Connection._unidentifiedStreams[outboundControlStream.StreamId]; Http3Api.TriggerTick(now); Http3Api.TriggerTick(now + limits.RequestHeadersTimeout); Assert.Equal((now + limits.RequestHeadersTimeout).Ticks, serverInboundControlStream.StreamTimeoutTicks); Http3Api.TriggerTick(now + limits.RequestHeadersTimeout + TimeSpan.FromTicks(1)); }
public async Task HEADERS_IncompleteFrameReceivedWithinRequestHeadersTimeout_StreamError() { var now = _serviceContext.MockSystemClock.UtcNow; var limits = _serviceContext.ServerOptions.Limits; var requestStream = await Http3Api.InitializeConnectionAndStreamsAsync(_noopApplication, null).DefaultTimeout(); var controlStream = await Http3Api.GetInboundControlStream().DefaultTimeout(); await controlStream.ExpectSettingsAsync().DefaultTimeout(); await requestStream.SendHeadersPartialAsync().DefaultTimeout(); await requestStream.OnStreamCreatedTask; var serverRequestStream = Http3Api.Connection._streams[requestStream.StreamId]; Http3Api.TriggerTick(now); Http3Api.TriggerTick(now + limits.RequestHeadersTimeout); Assert.Equal((now + limits.RequestHeadersTimeout).Ticks, serverRequestStream.StreamTimeoutTicks); Http3Api.TriggerTick(now + limits.RequestHeadersTimeout + TimeSpan.FromTicks(1)); await requestStream.WaitForStreamErrorAsync( Http3ErrorCode.RequestRejected, AssertExpectedErrorMessages, CoreStrings.BadRequest_RequestHeadersTimeout); }
private async Task <ConnectionContext> MakeRequestAsync(int index, KeyValuePair <string, string>[] headers) { var requestStream = await Http3Api.CreateRequestStream(); var streamContext = requestStream.StreamContext; await requestStream.SendHeadersAsync(headers); await requestStream.SendDataAsync(Encoding.ASCII.GetBytes($"Hello world {index}")); await requestStream.ExpectHeadersAsync(); var responseData = await requestStream.ExpectDataAsync(); Assert.Equal($"Hello world {index}", Encoding.ASCII.GetString(responseData.ToArray())); Assert.False(requestStream.Disposed, "Request is in progress and shouldn't be disposed."); await requestStream.SendDataAsync(Encoding.ASCII.GetBytes($"End {index}"), endStream : true); responseData = await requestStream.ExpectDataAsync(); Assert.Equal($"End {index}", Encoding.ASCII.GetString(responseData.ToArray())); await requestStream.ExpectReceiveEndOfStream(); await requestStream.OnStreamCompletedTask.DefaultTimeout(); await requestStream.OnDisposedTask.DefaultTimeout(); Assert.True(requestStream.Disposed, "Request is complete and should be disposed."); return(streamContext); }
public async Task GOAWAY_GracefulServerShutdown_SendsGoAway(int connectionRequests, int expectedStreamId) { await Http3Api.InitializeConnectionAsync(_echoApplication); var inboundControlStream = await Http3Api.GetInboundControlStream(); await inboundControlStream.ExpectSettingsAsync(); for (var i = 0; i < connectionRequests; i++) { var request = await Http3Api.CreateRequestStream(); await request.SendHeadersAsync(Headers); await request.EndStreamAsync(); await request.ExpectReceiveEndOfStream(); await request.OnStreamCompletedTask.DefaultTimeout(); } // Trigger server shutdown. Http3Api.CloseServerGracefully(); Assert.Null(await Http3Api.MultiplexedConnectionContext.AcceptAsync().DefaultTimeout()); await Http3Api.WaitForConnectionStopAsync(expectedStreamId, false, expectedErrorCode : Http3ErrorCode.NoError); }
public async Task GOAWAY_GracefulServerShutdownWithActiveRequest_SendsMultipleGoAways() { await Http3Api.InitializeConnectionAsync(_echoApplication); var inboundControlStream = await Http3Api.GetInboundControlStream(); await inboundControlStream.ExpectSettingsAsync(); var activeRequest = await Http3Api.CreateRequestStream(); await activeRequest.SendHeadersAsync(Headers); // Trigger server shutdown. Http3Api.CloseServerGracefully(); await Http3Api.WaitForGoAwayAsync(false, VariableLengthIntegerHelper.EightByteLimit); // Request made while shutting down is rejected. var rejectedRequest = await Http3Api.CreateRequestStream(); await rejectedRequest.WaitForStreamErrorAsync(Http3ErrorCode.RequestRejected); // End active request. await activeRequest.EndStreamAsync(); await activeRequest.ExpectReceiveEndOfStream(); // Client aborts the connection. Http3Api.MultiplexedConnectionContext.Abort(); await Http3Api.WaitForConnectionStopAsync(4, false, expectedErrorCode : Http3ErrorCode.NoError); }
public async Task StreamPool_VariableMultipleStreamsInSequence_PooledStreamReused(int count) { var headers = new[] { new KeyValuePair <string, string>(HeaderNames.Method, "Custom"), new KeyValuePair <string, string>(HeaderNames.Path, "/"), new KeyValuePair <string, string>(HeaderNames.Scheme, "http"), new KeyValuePair <string, string>(HeaderNames.Authority, "localhost:80"), }; await Http3Api.InitializeConnectionAsync(_echoApplication); ConnectionContext first = null; ConnectionContext last = null; for (var i = 0; i < count; i++) { Logger.LogInformation($"Iteration {i}"); var streamContext = await MakeRequestAsync(i, headers, sendData : true, waitForServerDispose : true); first ??= streamContext; last = streamContext; Assert.Same(first, last); } }
public async Task ControlStream_RequestHeadersTimeoutMaxValue_ExpirationIsMaxValue() { var now = _serviceContext.MockSystemClock.UtcNow; var limits = _serviceContext.ServerOptions.Limits; limits.RequestHeadersTimeout = TimeSpan.MaxValue; var headers = new[] { new KeyValuePair <string, string>(HeaderNames.Method, "Custom"), new KeyValuePair <string, string>(HeaderNames.Path, "/"), new KeyValuePair <string, string>(HeaderNames.Scheme, "http"), new KeyValuePair <string, string>(HeaderNames.Authority, "localhost:80"), }; await Http3Api.InitializeConnectionAsync(_noopApplication).DefaultTimeout(); var controlStream = await Http3Api.GetInboundControlStream().DefaultTimeout(); await controlStream.ExpectSettingsAsync().DefaultTimeout(); var outboundControlStream = await Http3Api.CreateControlStream(id : null); await outboundControlStream.OnStreamCreatedTask.DefaultTimeout(); var serverInboundControlStream = Http3Api.Connection._streams[outboundControlStream.StreamId]; Http3Api.TriggerTick(now); Assert.Equal(TimeSpan.MaxValue.Ticks, serverInboundControlStream.StreamTimeoutTicks); }
public async Task ControlStream_HeaderReceivedWithinRequestHeadersTimeout_StreamError() { var now = _serviceContext.MockSystemClock.UtcNow; var limits = _serviceContext.ServerOptions.Limits; var headers = new[] { new KeyValuePair <string, string>(HeaderNames.Method, "Custom"), new KeyValuePair <string, string>(HeaderNames.Path, "/"), new KeyValuePair <string, string>(HeaderNames.Scheme, "http"), new KeyValuePair <string, string>(HeaderNames.Authority, "localhost:80"), }; await Http3Api.InitializeConnectionAsync(_noopApplication).DefaultTimeout(); var controlStream = await Http3Api.GetInboundControlStream().DefaultTimeout(); await controlStream.ExpectSettingsAsync().DefaultTimeout(); var outboundControlStream = await Http3Api.CreateControlStream(id : null); await outboundControlStream.OnStreamCreatedTask.DefaultTimeout(); Http3Api.TriggerTick(now); Http3Api.TriggerTick(now + limits.RequestHeadersTimeout); await outboundControlStream.WriteStreamIdAsync(id : 0); await outboundControlStream.OnHeaderReceivedTask.DefaultTimeout(); Http3Api.TriggerTick(now + limits.RequestHeadersTimeout + TimeSpan.FromTicks(1)); }
public async Task DATA_Received_TooSlowlyOnSecondStream_AbortsConnectionAfterNonAdditiveRateTimeout() { var mockSystemClock = _serviceContext.MockSystemClock; var limits = _serviceContext.ServerOptions.Limits; // Use non-default value to ensure the min request and response rates aren't mixed up. limits.MinRequestBodyDataRate = new MinDataRate(480, TimeSpan.FromSeconds(2.5)); Http3Api._timeoutControl.Initialize(mockSystemClock.UtcNow.Ticks); await Http3Api.InitializeConnectionAsync(_readRateApplication); var inboundControlStream = await Http3Api.GetInboundControlStream(); await inboundControlStream.ExpectSettingsAsync(); var requestStream1 = await Http3Api.CreateRequestStream(); // _maxData is 16 KiB, and 16 KiB / 240 bytes/sec ~= 68 secs which is far above the grace period. await requestStream1.SendHeadersAsync(ReadRateRequestHeaders(_maxData.Length), endStream : false); await requestStream1.SendDataAsync(_maxData, endStream : true); await requestStream1.ExpectHeadersAsync(); await requestStream1.ExpectDataAsync(); await requestStream1.ExpectReceiveEndOfStream(); var requestStream2 = await Http3Api.CreateRequestStream(); await requestStream2.SendHeadersAsync(ReadRateRequestHeaders(_maxData.Length), endStream : false); await requestStream2.SendDataAsync(_maxData, endStream : false); await requestStream2.ExpectHeadersAsync(); await requestStream2.ExpectDataAsync(); // Due to the imprecision of floating point math and the fact that TimeoutControl derives rate from elapsed // time for reads instead of vice versa like for writes, use a half-second instead of single-tick cushion. var timeToReadMaxData = TimeSpan.FromSeconds(_maxData.Length / limits.MinRequestBodyDataRate.BytesPerSecond) - TimeSpan.FromSeconds(.5); // Don't send any more data and advance just to and then past the rate timeout. Http3Api.AdvanceClock(timeToReadMaxData); _mockTimeoutHandler.Verify(h => h.OnTimeout(It.IsAny <TimeoutReason>()), Times.Never); Http3Api.AdvanceClock(TimeSpan.FromSeconds(1)); _mockTimeoutHandler.Verify(h => h.OnTimeout(TimeoutReason.ReadDataRate), Times.Once); await Http3Api.WaitForConnectionErrorAsync <ConnectionAbortedException>( ignoreNonGoAwayFrames : false, expectedLastStreamId : null, Http3ErrorCode.InternalError, null); _mockTimeoutHandler.VerifyNoOtherCalls(); }
nameof(HeaderNames.Origin), "server.example.com")] // no authority public async Task WebTransportHandshake_IncorrectHeadersRejects(long error, string targetErrorMessage, params string[] headers) { _serviceContext.ServerOptions.EnableWebTransportAndH3Datagrams = true; await Http3Api.InitializeConnectionAsync(_noopApplication); var controlStream = await Http3Api.CreateControlStream(); var controlStream2 = await Http3Api.GetInboundControlStream(); var settings = new Http3PeerSettings() { EnableWebTransport = 1, H3Datagram = 1, }; await controlStream.SendSettingsAsync(settings.GetNonProtocolDefaults()); var response1 = await controlStream2.ExpectSettingsAsync(); await Http3Api.ServerReceivedSettingsReader.ReadAsync().DefaultTimeout(); Assert.Equal(1, response1[(long)Http3SettingType.EnableWebTransport]); var headersConnectFrame = new List <KeyValuePair <string, string> >(); for (var i = 0; i < headers.Length; i += 2) { headersConnectFrame.Add(new KeyValuePair <string, string>(GetHeaderFromName(headers[i]), headers[i + 1])); } var requestStream = await Http3Api.CreateRequestStream(headersConnectFrame); await requestStream.WaitForStreamErrorAsync((Http3ErrorCode)error, AssertExpectedErrorMessages, GetCoreStringFromName(targetErrorMessage)); }
public async Task HEADERS_Received_ContainsExpect100Continue_100ContinueSent() { await Http3Api.InitializeConnectionAsync(async context => { var buffer = new byte[16 * 1024]; var received = 0; while ((received = await context.Request.Body.ReadAsync(buffer, 0, buffer.Length)) > 0) { await context.Response.Body.WriteAsync(buffer, 0, received); } }); await Http3Api.CreateControlStream(); await Http3Api.GetInboundControlStream(); var requestStream = await Http3Api.CreateRequestStream(); var expectContinueRequestHeaders = new[] { new KeyValuePair <string, string>(HeaderNames.Method, "POST"), new KeyValuePair <string, string>(HeaderNames.Path, "/"), new KeyValuePair <string, string>(HeaderNames.Authority, "127.0.0.1"), new KeyValuePair <string, string>(HeaderNames.Scheme, "http"), new KeyValuePair <string, string>(HeaderNames.Expect, "100-continue"), }; await requestStream.SendHeadersAsync(expectContinueRequestHeaders); var frame = await requestStream.ReceiveFrameAsync(); Assert.Equal(Http3FrameType.Headers, frame.Type); var continueBytesQpackEncoded = new byte[] { 0x00, 0x00, 0xff, 0x00 }; Assert.Equal(continueBytesQpackEncoded, frame.PayloadSequence.ToArray()); await requestStream.SendDataAsync(Encoding.ASCII.GetBytes("Hello world"), endStream : false); var headers = await requestStream.ExpectHeadersAsync(); Assert.Equal("200", headers[HeaderNames.Status]); var responseData = await requestStream.ExpectDataAsync(); Assert.Equal("Hello world", Encoding.ASCII.GetString(responseData.ToArray())); Assert.False(requestStream.Disposed, "Request is in progress and shouldn't be disposed."); await requestStream.SendDataAsync(Encoding.ASCII.GetBytes($"End"), endStream : true); responseData = await requestStream.ExpectDataAsync(); Assert.Equal($"End", Encoding.ASCII.GetString(responseData.ToArray())); await requestStream.ExpectReceiveEndOfStream(); }
public async Task ResponseTrailers_MultipleStreams_Reset() { var requestHeaders = new[] { new KeyValuePair <string, string>(HeaderNames.Method, "GET"), new KeyValuePair <string, string>(HeaderNames.Path, "/hello"), new KeyValuePair <string, string>(HeaderNames.Scheme, "http"), new KeyValuePair <string, string>(HeaderNames.Authority, "localhost:80"), new KeyValuePair <string, string>(HeaderNames.ContentType, "application/json") }; var requestCount = 0; IHeaderDictionary trailersFirst = null; IHeaderDictionary trailersLast = null; await Http3Api.InitializeConnectionAsync(context => { var trailersFeature = context.Features.Get <IHttpResponseTrailersFeature>(); if (requestCount == 0) { trailersFirst = new ResponseTrailersWrapper(trailersFeature.Trailers); trailersFeature.Trailers = trailersFirst; } else { trailersLast = trailersFeature.Trailers; } trailersFeature.Trailers[$"trailer-{requestCount++}"] = "true"; return(Task.CompletedTask); }); for (int i = 0; i < 3; i++) { var requestStream = await Http3Api.CreateRequestStream(); await requestStream.SendHeadersAsync(requestHeaders, endStream : true); var responseHeaders = await requestStream.ExpectHeadersAsync(); var data = await requestStream.ExpectTrailersAsync(); Assert.Single(data); Assert.True(data.TryGetValue($"trailer-{i}", out var trailerValue) && bool.Parse(trailerValue)); await requestStream.ExpectReceiveEndOfStream(); await requestStream.OnDisposedTask.DefaultTimeout(); } Assert.NotNull(trailersFirst); Assert.NotNull(trailersLast); Assert.NotSame(trailersFirst, trailersLast); }
public async Task HEADERS_HeaderFrameReceivedWithinRequestHeadersTimeout_Success(bool pendingStreamsEnabled) { Http3Api._serviceContext.ServerOptions.EnableWebTransportAndH3Datagrams = pendingStreamsEnabled; var now = _serviceContext.MockSystemClock.UtcNow; var limits = _serviceContext.ServerOptions.Limits; var headers = new[] { new KeyValuePair <string, string>(InternalHeaderNames.Method, "Custom"), new KeyValuePair <string, string>(InternalHeaderNames.Path, "/"), new KeyValuePair <string, string>(InternalHeaderNames.Scheme, "http"), new KeyValuePair <string, string>(InternalHeaderNames.Authority, "localhost:80"), }; var requestStream = await Http3Api.InitializeConnectionAndStreamsAsync(_noopApplication, null).DefaultTimeout(); var controlStream = await Http3Api.GetInboundControlStream().DefaultTimeout(); await controlStream.ExpectSettingsAsync().DefaultTimeout(); dynamic serverRequestStream; if (pendingStreamsEnabled) { await requestStream.OnUnidentifiedStreamCreatedTask.DefaultTimeout(); serverRequestStream = Http3Api.Connection._unidentifiedStreams[requestStream.StreamId]; } else { await requestStream.OnStreamCreatedTask.DefaultTimeout(); serverRequestStream = Http3Api.Connection._streams[requestStream.StreamId]; } Http3Api.TriggerTick(now); Http3Api.TriggerTick(now + limits.RequestHeadersTimeout); Assert.Equal((now + limits.RequestHeadersTimeout).Ticks, serverRequestStream.StreamTimeoutTicks); await requestStream.SendHeadersAsync(headers).DefaultTimeout(); await requestStream.OnHeaderReceivedTask.DefaultTimeout(); Http3Api.TriggerTick(now + limits.RequestHeadersTimeout + TimeSpan.FromTicks(1)); await requestStream.SendDataAsync(Memory <byte> .Empty, endStream : true); await requestStream.ExpectHeadersAsync(); await requestStream.ExpectReceiveEndOfStream(); }
public async Task InboundStreams_CreateMultiple_ConnectionError(int streamId, string name) { await Http3Api.InitializeConnectionAsync(_noopApplication); await Http3Api.CreateControlStream(streamId); await Http3Api.CreateControlStream(streamId); await Http3Api.WaitForConnectionErrorAsync <Http3ConnectionErrorException>( ignoreNonGoAwayFrames : true, expectedLastStreamId : 0, expectedErrorCode : Http3ErrorCode.StreamCreationError, expectedErrorMessage : CoreStrings.FormatHttp3ControlStreamErrorMultipleInboundStreams(name)); }
public async Task KeepAliveTimeout_ControlStreamNotReceived_ConnectionClosed() { var limits = _serviceContext.ServerOptions.Limits; await Http3Api.InitializeConnectionAsync(_noopApplication).DefaultTimeout(); var controlStream = await Http3Api.GetInboundControlStream().DefaultTimeout(); await controlStream.ExpectSettingsAsync().DefaultTimeout(); Http3Api.AdvanceClock(limits.KeepAliveTimeout + TimeSpan.FromTicks(1)); await Http3Api.WaitForConnectionStopAsync(0, false, expectedErrorCode : Http3ErrorCode.NoError); }
public async Task GracefulServerShutdownClosesConnection() { await Http3Api.InitializeConnectionAsync(_echoApplication); var inboundControlStream = await Http3Api.GetInboundControlStream(); await inboundControlStream.ExpectSettingsAsync(); // Trigger server shutdown. Http3Api.CloseConnectionGracefully(); Assert.Null(await Http3Api.MultiplexedConnectionContext.AcceptAsync().DefaultTimeout()); await Http3Api.WaitForConnectionStopAsync(0, false, expectedErrorCode : Http3ErrorCode.NoError); }
public async Task ControlStream_ClientToServer_UnexpectedFrameType_ConnectionError(string frameType) { await Http3Api.InitializeConnectionAsync(_noopApplication); var controlStream = await Http3Api.CreateControlStream(); var f = Enum.Parse <Http3FrameType>(frameType); await controlStream.SendFrameAsync(f, Memory <byte> .Empty); await Http3Api.WaitForConnectionErrorAsync <Http3ConnectionErrorException>( ignoreNonGoAwayFrames : true, expectedLastStreamId : 0, expectedErrorCode : Http3ErrorCode.UnexpectedFrame, expectedErrorMessage : CoreStrings.FormatHttp3ErrorUnsupportedFrameOnControlStream(Http3Formatting.ToFormattedType(f))); }
public async Task ControlStream_ClientToServer_ClientCloses_ConnectionError() { await Http3Api.InitializeConnectionAsync(_noopApplication); var controlStream = await Http3Api.CreateControlStream(id : 0); await controlStream.SendSettingsAsync(new List <Http3PeerSetting>()); await controlStream.EndStreamAsync(); await Http3Api.WaitForConnectionErrorAsync <Http3ConnectionErrorException>( ignoreNonGoAwayFrames : true, expectedLastStreamId : 0, expectedErrorCode : Http3ErrorCode.ClosedCriticalStream, expectedErrorMessage : CoreStrings.Http3ErrorControlStreamClientClosedInbound); }
public async Task ResponseDrain_SlowerThanMinimumDataRate_AbortsConnection() { var now = _serviceContext.MockSystemClock.UtcNow; var limits = _serviceContext.ServerOptions.Limits; var mockSystemClock = _serviceContext.MockSystemClock; // Use non-default value to ensure the min request and response rates aren't mixed up. limits.MinResponseDataRate = new MinDataRate(480, TimeSpan.FromSeconds(2.5)); await Http3Api.InitializeConnectionAsync(_noopApplication); var inboundControlStream = await Http3Api.GetInboundControlStream(); await inboundControlStream.ExpectSettingsAsync(); var requestStream = await Http3Api.CreateRequestStream(); requestStream.StartStreamDisposeTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); await requestStream.SendHeadersAsync(new[] { new KeyValuePair <string, string>(HeaderNames.Path, "/"), new KeyValuePair <string, string>(HeaderNames.Scheme, "http"), new KeyValuePair <string, string>(HeaderNames.Method, "GET"), new KeyValuePair <string, string>(HeaderNames.Authority, "localhost:80"), }, endStream : true); await requestStream.OnDisposingTask.DefaultTimeout(); Http3Api.TriggerTick(now); Assert.Null(requestStream.StreamContext._error); Http3Api.TriggerTick(now + TimeSpan.FromTicks(1)); Assert.Null(requestStream.StreamContext._error); Http3Api.TriggerTick(now + limits.MinResponseDataRate.GracePeriod + TimeSpan.FromTicks(1)); requestStream.StartStreamDisposeTcs.TrySetResult(); await Http3Api.WaitForConnectionErrorAsync <ConnectionAbortedException>( ignoreNonGoAwayFrames : false, expectedLastStreamId : 4, Http3ErrorCode.InternalError, expectedErrorMessage : CoreStrings.ConnectionTimedBecauseResponseMininumDataRateNotSatisfied); Assert.Contains(TestSink.Writes, w => w.EventId.Name == "ResponseMinimumDataRateNotSatisfied"); }
public async Task DATA_Received_SlowlyWhenRateLimitDisabledPerRequest_DoesNotAbortConnection() { var mockSystemClock = _serviceContext.MockSystemClock; var limits = _serviceContext.ServerOptions.Limits; // Use non-default value to ensure the min request and response rates aren't mixed up. limits.MinRequestBodyDataRate = new MinDataRate(480, TimeSpan.FromSeconds(2.5)); Http3Api._timeoutControl.Initialize(mockSystemClock.UtcNow.Ticks); await Http3Api.InitializeConnectionAsync(context => { // Completely disable rate limiting for this stream. context.Features.Get <IHttpMinRequestBodyDataRateFeature>().MinDataRate = null; return(_readRateApplication(context)); }); var inboundControlStream = await Http3Api.GetInboundControlStream(); await inboundControlStream.ExpectSettingsAsync(); Http3Api.OutboundControlStream = await Http3Api.CreateControlStream(); // _helloWorldBytes is 12 bytes, and 12 bytes / 240 bytes/sec = .05 secs which is far below the grace period. var requestStream = await Http3Api.CreateRequestStream(ReadRateRequestHeaders(_helloWorldBytes.Length), endStream : false); await requestStream.SendDataAsync(_helloWorldBytes, endStream : false); await requestStream.ExpectHeadersAsync(); await requestStream.ExpectDataAsync(); // Don't send any more data and advance just to and then past the grace period. Http3Api.AdvanceClock(limits.MinRequestBodyDataRate.GracePeriod); _mockTimeoutHandler.Verify(h => h.OnTimeout(It.IsAny <TimeoutReason>()), Times.Never); Http3Api.AdvanceClock(TimeSpan.FromTicks(1)); _mockTimeoutHandler.Verify(h => h.OnTimeout(It.IsAny <TimeoutReason>()), Times.Never); await requestStream.SendDataAsync(_helloWorldBytes, endStream : true); await requestStream.ExpectReceiveEndOfStream(); _mockTimeoutHandler.VerifyNoOtherCalls(); }
public async Task CreateRequestStream_RequestCompleted_Disposed() { var appCompletedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); await Http3Api.InitializeConnectionAsync(async context => { var buffer = new byte[16 * 1024]; var received = 0; while ((received = await context.Request.Body.ReadAsync(buffer, 0, buffer.Length)) > 0) { await context.Response.Body.WriteAsync(buffer, 0, received); } await appCompletedTcs.Task; }); await Http3Api.CreateControlStream(); await Http3Api.GetInboundControlStream(); var requestStream = await Http3Api.CreateRequestStream(); var headers = new[] { new KeyValuePair <string, string>(HeaderNames.Method, "Custom"), new KeyValuePair <string, string>(HeaderNames.Path, "/"), new KeyValuePair <string, string>(HeaderNames.Scheme, "http"), new KeyValuePair <string, string>(HeaderNames.Authority, "localhost:80"), }; await requestStream.SendHeadersAsync(headers); await requestStream.SendDataAsync(Encoding.ASCII.GetBytes("Hello world"), endStream : true); Assert.False(requestStream.Disposed); appCompletedTcs.SetResult(); await requestStream.ExpectHeadersAsync(); var responseData = await requestStream.ExpectDataAsync(); Assert.Equal("Hello world", Encoding.ASCII.GetString(responseData.ToArray())); await requestStream.OnDisposedTask.DefaultTimeout(); Assert.True(requestStream.Disposed); }
public async Task StreamPool_MultipleStreamsInSequence_PooledStreamReused() { var headers = new[] { new KeyValuePair <string, string>(HeaderNames.Method, "Custom"), new KeyValuePair <string, string>(HeaderNames.Path, "/"), new KeyValuePair <string, string>(HeaderNames.Scheme, "http"), new KeyValuePair <string, string>(HeaderNames.Authority, "localhost:80"), }; await Http3Api.InitializeConnectionAsync(_echoApplication); var streamContext1 = await MakeRequestAsync(0, headers, sendData : true, waitForServerDispose : true); var streamContext2 = await MakeRequestAsync(1, headers, sendData : true, waitForServerDispose : true); Assert.Same(streamContext1, streamContext2); }
public async Task ControlStream_ServerToClient_ErrorInitializing_ConnectionError() { Http3Api.OnCreateServerControlStream = testStreamContext => { var controlStream = new Microsoft.AspNetCore.Testing.Http3ControlStream(Http3Api, testStreamContext); // Make server connection error when trying to write to control stream. controlStream.StreamContext.Transport.Output.Complete(); return(controlStream); }; await Http3Api.InitializeConnectionAsync(_noopApplication); Http3Api.AssertConnectionError <Http3ConnectionErrorException>( expectedErrorCode: Http3ErrorCode.ClosedCriticalStream, expectedErrorMessage: CoreStrings.Http3ControlStreamErrorInitializingOutbound); }
public async Task WebTransportHandshake_ClientToServerPasses() { _serviceContext.ServerOptions.EnableWebTransportAndH3Datagrams = true; await Http3Api.InitializeConnectionAsync(_noopApplication); var controlStream = await Http3Api.CreateControlStream(); var controlStream2 = await Http3Api.GetInboundControlStream(); var settings = new Http3PeerSettings() { EnableWebTransport = 1, H3Datagram = 1, }; await controlStream.SendSettingsAsync(settings.GetNonProtocolDefaults()); var response1 = await controlStream2.ExpectSettingsAsync(); await Http3Api.ServerReceivedSettingsReader.ReadAsync().DefaultTimeout(); Assert.Equal(1, response1[(long)Http3SettingType.EnableWebTransport]); var requestStream = await Http3Api.CreateRequestStream(); var headersConnectFrame = new[] { new KeyValuePair <string, string>(HeaderNames.Method, "CONNECT"), new KeyValuePair <string, string>(HeaderNames.Protocol, "webtransport"), new KeyValuePair <string, string>(HeaderNames.Scheme, "http"), new KeyValuePair <string, string>(HeaderNames.Path, "/"), new KeyValuePair <string, string>(HeaderNames.Authority, "server.example.com"), new KeyValuePair <string, string>(HeaderNames.Origin, "server.example.com") }; await requestStream.SendHeadersAsync(headersConnectFrame); var response2 = await requestStream.ExpectHeadersAsync(); Assert.Equal((int)HttpStatusCode.OK, Convert.ToInt32(response2[HeaderNames.Status], null)); await requestStream.OnDisposedTask.DefaultTimeout(); }
public async Task DATA_Sent_TooSlowlyDueToSocketBackPressureOnLargeWrite_AbortsConnectionAfterRateTimeout() { var mockSystemClock = _serviceContext.MockSystemClock; var limits = _serviceContext.ServerOptions.Limits; // Use non-default value to ensure the min request and response rates aren't mixed up. limits.MinResponseDataRate = new MinDataRate(480, TimeSpan.FromSeconds(2.5)); // Disable response buffering so "socket" backpressure is observed immediately. limits.MaxResponseBufferSize = 0; Http3Api._timeoutControl.Initialize(mockSystemClock.UtcNow.Ticks); var app = new EchoAppWithNotification(); var requestStream = await Http3Api.InitializeConnectionAndStreamsAsync(app.RunApp); await requestStream.SendHeadersAsync(_browserRequestHeaders, endStream : false); await requestStream.SendDataAsync(_maxData, endStream : true); await requestStream.ExpectHeadersAsync(); await app.WriteStartedTask.DefaultTimeout(); // Complete timing of the request body so we don't induce any unexpected request body rate timeouts. Http3Api._timeoutControl.Tick(mockSystemClock.UtcNow); var timeToWriteMaxData = TimeSpan.FromSeconds((requestStream.BytesReceived + _maxData.Length) / limits.MinResponseDataRate.BytesPerSecond) + limits.MinResponseDataRate.GracePeriod + Heartbeat.Interval - TimeSpan.FromSeconds(.5); // Don't read data frame to induce "socket" backpressure. Http3Api.AdvanceClock(timeToWriteMaxData); _mockTimeoutHandler.Verify(h => h.OnTimeout(It.IsAny <TimeoutReason>()), Times.Never); Http3Api.AdvanceClock(TimeSpan.FromSeconds(1)); _mockTimeoutHandler.Verify(h => h.OnTimeout(TimeoutReason.WriteDataRate), Times.Once); // The _maxData bytes are buffered from before the timeout, but not an END_STREAM data frame. await requestStream.ExpectDataAsync(); _mockTimeoutHandler.VerifyNoOtherCalls(); }
public async Task HEADERS_CookiesMergedIntoOne() { var requestHeaders = new[] { new KeyValuePair <string, string>(HeaderNames.Method, "GET"), new KeyValuePair <string, string>(HeaderNames.Path, "/"), new KeyValuePair <string, string>(HeaderNames.Scheme, "http"), new KeyValuePair <string, string>(HeaderNames.Cookie, "a=0"), new KeyValuePair <string, string>(HeaderNames.Cookie, "b=1"), new KeyValuePair <string, string>(HeaderNames.Cookie, "c=2"), }; var receivedHeaders = ""; await Http3Api.InitializeConnectionAsync(async context => { var buffer = new byte[16 * 1024]; var received = 0; // verify that the cookies are all merged into a single string receivedHeaders = context.Request.Headers[HeaderNames.Cookie]; while ((received = await context.Request.Body.ReadAsync(buffer, 0, buffer.Length)) > 0) { await context.Response.Body.WriteAsync(buffer, 0, received); } }); await Http3Api.CreateControlStream(); await Http3Api.GetInboundControlStream(); var requestStream = await Http3Api.CreateRequestStream(); await requestStream.SendHeadersAsync(requestHeaders, endStream : true); var responseHeaders = await requestStream.ExpectHeadersAsync(); await requestStream.ExpectReceiveEndOfStream(); await requestStream.OnDisposedTask.DefaultTimeout(); Assert.Equal("a=0; b=1; c=2", receivedHeaders); }
public async Task SETTINGS_ReservedSettingSent_ConnectionError(long settingIdentifier) { await Http3Api.InitializeConnectionAsync(_echoApplication); var outboundcontrolStream = await Http3Api.CreateControlStream(); await outboundcontrolStream.SendSettingsAsync(new List <Http3PeerSetting> { new Http3PeerSetting((Http3SettingType)settingIdentifier, 0) // reserved value }); await Http3Api.GetInboundControlStream(); await Http3Api.WaitForConnectionErrorAsync <Http3ConnectionErrorException>( ignoreNonGoAwayFrames : true, expectedLastStreamId : 0, expectedErrorCode : Http3ErrorCode.SettingsError, expectedErrorMessage : CoreStrings.FormatHttp3ErrorControlStreamReservedSetting($"0x{settingIdentifier.ToString("X", CultureInfo.InvariantCulture)}")); }