public async Task ReceivingDataDirectlyAfterInformationalHeadersShouldBeAnError() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var res = await StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe); // Send informational headers to the client var infoHeaders = new HeaderField[] { new HeaderField { Name = ":status", Value = "100" }, new HeaderField { Name = "extension-field", Value = "bar" }, }; await inPipe.WriteHeaders(res.hEncoder, 1u, false, infoHeaders); var recvdHeaders = await res.stream.ReadHeadersAsync(); Assert.True(infoHeaders.SequenceEqual(recvdHeaders)); // Try to send data await inPipe.WriteData(1u, 100, null, true); // Expect to receive an error await outPipe.AssertResetStreamReception(1u, ErrorCode.ProtocolError); Assert.Equal(StreamState.Reset, res.stream.State); await Assert.ThrowsAsync <StreamResetException>( () => res.stream.ReadHeadersAsync()); }
public async Task ADataFrameOnAnUnknownStreamIdShouldTriggerAStreamReset( bool isServer, uint streamId) { // TODO: Add test cases for clients var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); Func <IStream, bool> listener = (s) => true; var http2Con = await ConnectionUtils.BuildEstablishedConnection( isServer, inPipe, outPipe, loggerProvider, listener); // Establish a high stream ID, which means all below are invalid var hEncoder = new Encoder(); var createdStreamId = 111u; if (!isServer) { throw new Exception("For clients the stream must be created from connection"); } await inPipe.WriteHeaders( hEncoder, createdStreamId, false, DefaultGetHeaders); await inPipe.WriteData(streamId, 0); await outPipe.AssertResetStreamReception(streamId, ErrorCode.StreamClosed); }
public async Task SendingInvalidTrailersShouldTriggerAStreamReset() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var headers = new HeaderField[] { new HeaderField { Name = ":method", Value = "GET" }, new HeaderField { Name = ":scheme", Value = "http" }, new HeaderField { Name = ":path", Value = "/" }, }; var trailers = new HeaderField[] { new HeaderField { Name = ":method", Value = "GET" }, }; Func <IStream, bool> listener = (s) => true; var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider, listener); var hEncoder = new Encoder(); await inPipe.WriteHeaders(hEncoder, 1, false, headers); await inPipe.WriteHeaders(hEncoder, 1, true, trailers); await outPipe.AssertResetStreamReception(1, ErrorCode.ProtocolError); }
public async Task ReceivingHeadersOrDataOnAClosedStreamShouldTriggerAStreamReset( StreamState streamState, bool sendHeaders, bool sendData, bool sendTrailers) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var localCloseDone = new SemaphoreSlim(0); var res = await StreamCreator.CreateConnectionAndStream( streamState, loggerProvider, inPipe, outPipe); if (sendHeaders) { await inPipe.WriteHeaders(res.hEncoder, 1, false, DefaultGetHeaders); } if (sendData) { await inPipe.WriteData(1u, 0); } if (sendTrailers) { await inPipe.WriteHeaders(res.hEncoder, 1, true, DefaultGetHeaders); } await outPipe.AssertResetStreamReception(1u, ErrorCode.StreamClosed); var expectedState = streamState == StreamState.Closed ? StreamState.Closed : StreamState.Reset; Assert.Equal(expectedState, res.stream.State); }
public async Task CancellingAStreamShouldSendAResetFrame( bool isServer) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var r = await StreamCreator.CreateConnectionAndStream( isServer, loggerProvider, inPipe, outPipe); r.stream.Cancel(); await outPipe.AssertResetStreamReception(1, ErrorCode.Cancel); Assert.Equal(StreamState.Reset, r.stream.State); }
public async Task HeadersOnStreamIdWhichCanNotBeRemoteInitiatedShouldTriggerAStreamReset( bool isServer, uint streamId) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); Func <IStream, bool> listener = (s) => true; var http2Con = await ConnectionUtils.BuildEstablishedConnection( isServer, inPipe, outPipe, loggerProvider, listener); var hEncoder = new Encoder(); await inPipe.WriteHeaders(hEncoder, streamId, false, DefaultGetHeaders); await outPipe.AssertResetStreamReception(streamId, ErrorCode.StreamClosed); }
public async Task ReceivingHeadersWithNoAttachedListenerOrRefusingListenerShouldTriggerStreamReset( bool noListener) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); Func<IStream, bool> listener = null; if (!noListener) listener = (s) => false; var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider, listener); var hEncoder = new Encoder(); await inPipe.WriteHeaders(hEncoder, 1, false, DefaultGetHeaders); await outPipe.AssertResetStreamReception(1, ErrorCode.RefusedStream); }
public async Task ShouldResetStreamWhenStreamFlowControlWindowIsOverloaded( int amount) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); Func <IStream, bool> listener = (s) => true; var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider, listener); var hEncoder = new Encoder(); await inPipe.WriteHeaders(hEncoder, 1, false, DefaultGetHeaders); await inPipe.WriteWindowUpdate(1, amount); await outPipe.AssertResetStreamReception(1, ErrorCode.FlowControlError); }
public async Task ReceivingHeadersWithATooSmallStreamIdShouldTriggerAStreamReset( bool noListener) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); Func<IStream, bool> listener = (s) => true; var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider, listener); var hEncoder = new Encoder(); await inPipe.WriteHeaders(hEncoder, 33, false, DefaultGetHeaders); await inPipe.WriteHeaders(hEncoder, 31, false, DefaultGetHeaders); // Remark: The specification actually wants a connection error / GOAWAY // to be emitted. However as the implementation can not safely determine // if that stream ID was never used and valid we send and check for // a stream reset. await outPipe.AssertResetStreamReception(31, ErrorCode.StreamClosed); }
public async Task ReceivingHeaders2TimesShouldTriggerAStreamReset( bool isServer, bool headersAreEndOfStream) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); // Establish open streams, which means headers are sent in both // directions var res = await StreamCreator.CreateConnectionAndStream( isServer, loggerProvider, inPipe, outPipe); // Write a second header await inPipe.WriteHeaders( res.hEncoder, 1, headersAreEndOfStream, DefaultGetHeaders); await outPipe.AssertResetStreamReception(1, ErrorCode.ProtocolError); Assert.Equal(StreamState.Reset, res.stream.State); }
public async Task ReceivingDataBeforeHeadersShouldYieldAResetException() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var res = await StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe); await inPipe.WriteData(1u, 1); await outPipe.AssertResetStreamReception(1u, ErrorCode.ProtocolError); var ex = await Assert.ThrowsAsync <AggregateException>( () => res.stream.ReadWithTimeout(new ArraySegment <byte>( new byte[1]))); Assert.IsType <StreamResetException>(ex.InnerException); Assert.Equal(StreamState.Reset, res.stream.State); }
public async Task HeadersWithContentLengthShouldForceDataLengthValidation( int contentLen, int[] dataLength, bool useTrailers, bool shouldError) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); Func <IStream, bool> listener = (s) => true; var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider, listener); var headers = TestHeaders.DefaultGetHeaders.Append( new HeaderField { Name = "content-length", Value = contentLen.ToString() }); var hEncoder = new Encoder(); await inPipe.WriteHeaders(hEncoder, 1, false, headers); for (var i = 0; i < dataLength.Length; i++) { var isEos = i == (dataLength.Length - 1) && !useTrailers; await inPipe.WriteData(1u, dataLength[i], endOfStream : isEos); } if (useTrailers) { await inPipe.WriteHeaders(hEncoder, 1, true, new HeaderField[0]); } if (shouldError) { await outPipe.AssertResetStreamReception(1u, ErrorCode.ProtocolError); } else { await inPipe.WritePing(new byte[8], false); await outPipe.ReadAndDiscardPong(); } }
public async Task NewStreamsAfterGoAwayShouldBeRejected() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var res = await ServerStreamTests.StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe); // Start the GoAway process await res.conn.GoAwayAsync(ErrorCode.NoError, false); // Expect the GoAway message await outPipe.AssertGoAwayReception(ErrorCode.NoError, 1u); // Try to establish a new stream var hEncoder = new Http2.Hpack.Encoder(); await inPipe.WriteHeaders(hEncoder, 3, true, TestHeaders.DefaultGetHeaders); // Expect a stream rejection await outPipe.AssertResetStreamReception(3u, ErrorCode.RefusedStream); }
public async Task ReceivingWindowUpdatesWith0AmountShouldTriggerGoAwayOrReset( uint streamId) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var res = await ServerStreamTests.StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe); await inPipe.WriteWindowUpdate(streamId, 0); if (streamId == 0) { await outPipe.AssertGoAwayReception(ErrorCode.ProtocolError, 1); await outPipe.AssertStreamEnd(); } else { await outPipe.AssertResetStreamReception(1u, ErrorCode.ProtocolError); } }
public async Task ReceivingHeadersOrDataOnAResetStreamShouldProduceAClosedStreamError( bool sendHeaders, bool sendData, bool sendTrailers) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var r = await StreamCreator.CreateConnectionAndStream( StreamState.Reset, loggerProvider, inPipe, outPipe); if (sendHeaders) { await inPipe.WriteHeaders(r.hEncoder, 1, false, DefaultGetHeaders); } if (sendData) { await inPipe.WriteData(1u, 0); } if (sendTrailers) { await inPipe.WriteHeaders(r.hEncoder, 1, true, DefaultGetHeaders); } await outPipe.AssertResetStreamReception(1, ErrorCode.StreamClosed); }
public async Task ViolationsOfTheStreamFlowControlWindowShouldBeDetected( int?padLen, int streamWindowSize, int dataAmount, bool isError) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); // Use a smaller flow control window for streams so that the stream // window errors. var settings = Settings.Default; settings.InitialWindowSize = (uint)streamWindowSize; settings.MaxFrameSize = 1024 * 1024; // The test values for the amount of data to be sent on the stream // are tuned small enough that no connection window // updates will be sent, which would fail the expected output. var res = await ServerStreamTests.StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe, localSettings : settings); // Write more data than the stream allows await inPipe.WriteData(1u, dataAmount, padLen : padLen); if (isError) { // Expect that the stream got reset await outPipe.AssertResetStreamReception(1u, ErrorCode.FlowControlError); Assert.Equal(StreamState.Reset, res.stream.State); } // Send a ping afterwards, which should be processed in all cases await inPipe.WritePing(new byte[8], false); await outPipe.ReadAndDiscardPong(); }
public async Task IncomingStreamsAfterMaxConcurrentStreamsShouldBeRejected( int maxConcurrentStreams) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var acceptedStreams = new List<IStream>(); Func<IStream, bool> listener = (s) => { lock (acceptedStreams) { acceptedStreams.Add(s); } return true; }; var settings = Settings.Default; settings.MaxConcurrentStreams = (uint)maxConcurrentStreams; var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider, listener, localSettings: settings); var hEncoder = new Encoder(); // Open maxConcurrentStreams var streamId = 1u; for (var i = 0; i < maxConcurrentStreams; i++) { await inPipe.WriteHeaders(hEncoder, streamId, false, DefaultGetHeaders); streamId += 2; } // Assert no rejection and response so far await inPipe.WritePing(new byte[8], false); await outPipe.ReadAndDiscardPong(); lock (acceptedStreams) { Assert.Equal(maxConcurrentStreams, acceptedStreams.Count); } // Try to open an additional stream await inPipe.WriteHeaders(hEncoder, streamId, false, DefaultGetHeaders); // This one should be rejected await outPipe.AssertResetStreamReception(streamId, ErrorCode.RefusedStream); lock (acceptedStreams) { Assert.Equal(maxConcurrentStreams, acceptedStreams.Count); } // Once a stream is closed a new one should be acceptable await inPipe.WriteResetStream(streamId-2, ErrorCode.Cancel); streamId += 2; await inPipe.WriteHeaders(hEncoder, streamId, false, DefaultGetHeaders); // Assert no error response await inPipe.WritePing(new byte[8], false); await outPipe.ReadAndDiscardPong(); lock (acceptedStreams) { // +1 because the dead stream isn't removed Assert.Equal(maxConcurrentStreams+1, acceptedStreams.Count); // Check if the reset worked Assert.Equal( StreamState.Reset, acceptedStreams[acceptedStreams.Count-2].State); } }
public async Task ViolationsOfTheConnectionFlowControlWindowShouldBeDetected( int?padLen, uint streamId, int dataAmount, bool isError) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); // Use a bigger flow control window for streams so that the connection // window errors and streams dont send window updates. // Also update maxFrameSize, otherwise the connection will send // window updates faster than we can violate the contract var settings = Settings.Default; settings.InitialWindowSize = 256 * 1024; settings.MaxFrameSize = 1024 * 1024; var res = await ServerStreamTests.StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe, localSettings : settings); // Open an additional stream, so that streamId 3 is not in the IDLE // range, which causes a connection error await inPipe.WriteHeaders( res.hEncoder, 555u, false, DefaultGetHeaders); // Try to send the data // This might fail, if the connection goes away before // everything is read try { await inPipe.WriteData(streamId, dataAmount, padLen : padLen); } catch (Exception e) { if (!isError || !(e is TimeoutException)) { throw; } } if (isError) { await outPipe.AssertGoAwayReception(ErrorCode.FlowControlError, 555u); await outPipe.AssertStreamEnd(); await res.conn.Done; Assert.Equal(StreamState.Reset, res.stream.State); } else { // Expect the connection to be alive await outPipe.AssertWindowUpdate(0u, 65535); if (streamId != 1) { // We expect a reset for the unknown stream on which data // was transmitted await outPipe.AssertResetStreamReception(streamId, ErrorCode.StreamClosed); } await inPipe.WritePing(new byte[8], false); await outPipe.ReadAndDiscardPong(); Assert.Equal(StreamState.Open, res.stream.State); } }