public async Task ShouldAllowToSetTheMaxPossibleConnectionFlowControlWindowSize() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider, null); var amount = int.MaxValue - 65535; await inPipe.WriteWindowUpdate(0, amount); // Check aliveness with ping/pong await inPipe.WritePing(new byte[8], false); await outPipe.ReadAndDiscardPong(); }
public async Task ConnectionShouldIgnoreUnsolicitedPingAcks() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider); // Write ping ACK var pingData = new byte[8]; for (var i = 0; i < pingData.Length; i++) { pingData[i] = (byte)i; } await inPipe.WritePing(pingData, true); // Expect no reaction await outPipe.AssertReadTimeout(); }
public async Task EmptyDataFramesShouldBeValidSeperatorsBeforeTrailers( bool isServer) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var res = await StreamCreator.CreateConnectionAndStream( isServer, loggerProvider, inPipe, outPipe); // Send a 0byte data frame await inPipe.WriteData(1u, 0); // Send trailers await inPipe.WriteHeaders(res.hEncoder, 1u, true, DefaultTrailingHeaders); // Check for no stream error await inPipe.WritePing(new byte[8], false); await outPipe.ReadAndDiscardPong(); }
public async Task ShouldAllowToSetTheMaxPossibleStreamFlowControlWindowSize() { 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); var amount = int.MaxValue - 65535; await inPipe.WriteWindowUpdate(1, amount); // Check aliveness with ping/pong await inPipe.WritePing(new byte[8], false); await outPipe.ReadAndDiscardPong(); }
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 ReceivingEmptyDataFramesShouldBePossibleIfStreamWindowIsDrained( bool emptyFrameIsEndOfStream) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var localSettings = Settings.Default; localSettings.InitialWindowSize = 10; var res = await ServerStreamTests.StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe, localSettings : localSettings); // Consume the complete stream flow control window await inPipe.WriteData(1u, 10); // Send a 0byte data frame await inPipe.WriteData(1u, 0, endOfStream : emptyFrameIsEndOfStream); // Check if we are still alive await inPipe.WritePing(new byte[8], false); await outPipe.ReadAndDiscardPong(); }
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 ConnectionShouldIgnoreUnknownFrames( bool isServer, int payloadLength) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( isServer, inPipe, outPipe, loggerProvider); // send an undefined frame type var fh = new FrameHeader { Type = (FrameType)100, Flags = 33, Length = payloadLength, StreamId = 0, }; await inPipe.WriteFrameHeader(fh); if (payloadLength != 0) { var payload = new byte[payloadLength]; await inPipe.WriteAsync(new ArraySegment <byte>(payload)); } // Send a ping afterwards // If we get a response the unknown frame in between was ignored var pingData = new byte[8]; for (var i = 0; i < 8; i++) { pingData[i] = (byte)i; } await inPipe.WritePing(pingData, false); await outPipe.ReadAndDiscardPong(); }
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); } }
public async Task MaxHeaderListSizeViolationsShouldBeDetected( uint maxHeaderListSize, int headersInFirstFrame, int headersInContFrame, bool shouldError) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); Func <IStream, bool> listener = (s) => true; var settings = Settings.Default; settings.MaxHeaderListSize = maxHeaderListSize; var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider, listener, localSettings : settings, huffmanStrategy : HuffmanStrategy.Never); var hEncoder = new Encoder(); var headers = new List <HeaderField>(); // Add the default headers // These take 123 bytes in store and 3 bytes in transmission headers.AddRange(new HeaderField[] { new HeaderField { Name = ":method", Value = "GET" }, new HeaderField { Name = ":path", Value = "/" }, new HeaderField { Name = ":scheme", Value = "http" }, }); var currentHeadersLength = headers .Select(hf => hf.Name.Length + hf.Value.Length + 32) .Sum(); // Create a header which takes 34 bytes in store and 5 bytes in transmission var extraHeader = new HeaderField { Name = "a", Value = "b", Sensitive = true }; while (currentHeadersLength < headersInFirstFrame) { headers.Add(extraHeader); currentHeadersLength += 1 + 1 + 32; } await inPipe.WriteHeaders( hEncoder, 1, false, headers, headersInContFrame == 0); if (headersInContFrame != 0) { headers.Clear(); currentHeadersLength = 0; while (currentHeadersLength < headersInContFrame) { headers.Add(extraHeader); currentHeadersLength += 1 + 1 + 32; } await inPipe.WriteContinuation( hEncoder, 1, headers, true); } if (shouldError) { // TODO: The spec says actually the remote should answer with // an HTTP431 error - but that happens on another layer await outPipe.AssertGoAwayReception(ErrorCode.ProtocolError, 0u); await outPipe.AssertStreamEnd(); } else { 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); } }