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 ConnectionShouldIgnoreResetsforUnknownStreams() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); Func <IStream, bool> listener = (s) => true; var conn = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider, listener); var hEncoder = new Encoder(); var streamId = 7u; await inPipe.WriteHeaders( hEncoder, streamId, false, TestHeaders.DefaultGetHeaders); await inPipe.WriteResetStream(streamId - 2, ErrorCode.RefusedStream); await inPipe.WriteResetStream(streamId - 4, ErrorCode.Cancel); // Send a ping afterwards // If we get a response the reset frame in between was ignored await inPipe.WritePing(new byte[8], false); await outPipe.ReadAndDiscardPong(); }
public async Task ConnectionShouldRespondToPingWithPong(bool isServer) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( isServer, inPipe, outPipe, loggerProvider); var pingData = new byte[8]; for (var i = 0; i < pingData.Length; i++) { pingData[i] = (byte)i; } await inPipe.WritePing(pingData, false); var res = await outPipe.ReadFrameHeaderWithTimeout(); Assert.Equal(FrameType.Ping, res.Type); Assert.Equal(0u, res.StreamId); Assert.Equal(8, res.Length); Assert.Equal((byte)PingFrameFlags.Ack, res.Flags); var pongData = new byte[8]; await outPipe.ReadAllWithTimeout(new ArraySegment <byte>(pongData)); for (var i = 0; i < pingData.Length; i++) { Assert.Equal((byte)i, pongData[i]); } }
public async Task ConnectionShouldCloseAndSignalDoneInCaseOfAProtocolError(bool isServer) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( isServer, inPipe, outPipe, loggerProvider); // Cause a protocol error var fh = new FrameHeader { Type = FrameType.Data, StreamId = 0u, Flags = 0, Length = 0, }; await inPipe.WriteFrameHeader(fh); await outPipe.AssertGoAwayReception(ErrorCode.ProtocolError, 0u); await outPipe.AssertStreamEnd(); await inPipe.CloseAsync(); // Expect the connection to close within timeout var closed = http2Con.Done; Assert.True( closed == await Task.WhenAny(closed, Task.Delay(1000)), "Expected connection to close"); }
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 ConnectionShouldCloseAndSignalDoneWhenWritingToOutputFails(bool isServer) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var failableOutPipe = new FailingPipe(outPipe); var http2Con = await ConnectionUtils.BuildEstablishedConnection( isServer, inPipe, failableOutPipe, loggerProvider); // Make the next write attempt fail failableOutPipe.FailNextWrite = true; // Send something which triggers a response await inPipe.WritePing(new byte[8], false); // Wait for the connection to close the outgoing part await outPipe.AssertStreamEnd(); Assert.True(failableOutPipe.CloseCalled); // If the connection was successfully closed close the incoming data // stream, since this is expected from a bidirectional stream implementation await inPipe.CloseAsync(); // Expect the connection to close within timeout var closed = http2Con.Done; Assert.True( closed == await Task.WhenAny(closed, Task.Delay(1000)), "Expected connection to close"); }
public async Task ReceivingHeadersWithEndOfStreamShouldAllowToReadEndOfStream() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var handlerDone = new SemaphoreSlim(0); var gotEos = false; var streamStateOk = false; Func<IStream, bool> listener = (s) => { Task.Run(async () => { await s.ReadHeadersAsync(); var buf = new byte[1024]; var res = await s.ReadAsync(new ArraySegment<byte>(buf)); gotEos = res.EndOfStream && res.BytesRead == 0; streamStateOk = s.State == StreamState.HalfClosedRemote; handlerDone.Release(); }); return true; }; var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider, listener); var hEncoder = new Encoder(); await inPipe.WriteHeaders(hEncoder, 1, true, DefaultGetHeaders); var requestDone = await handlerDone.WaitAsync(ReadableStreamTestExtensions.ReadTimeout); Assert.True(requestDone, "Expected handler to complete within timeout"); Assert.True(gotEos); Assert.True(streamStateOk); }
public async Task ConnectionShouldGoAwayOnInvalidGoAwayFrameLength( bool isServer, int frameLength) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( isServer, inPipe, outPipe, loggerProvider); var fh = new FrameHeader { Type = FrameType.GoAway, Flags = 0, Length = frameLength, StreamId = 0, }; await inPipe.WriteFrameHeader(fh); var expectedErr = frameLength > 65535 ? ErrorCode.FrameSizeError : ErrorCode.ProtocolError; await outPipe.AssertGoAwayReception(expectedErr, 0); await outPipe.AssertStreamEnd(); }
public async Task ConnectionShouldIgnorePriorityData( bool isServer, uint streamId, uint streamDependency, bool isExclusive, byte weight) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( isServer, inPipe, outPipe, loggerProvider); var prioData = new PriorityData { StreamDependency = streamDependency, StreamDependencyIsExclusive = isExclusive, Weight = weight, }; await inPipe.WritePriority(streamId, prioData); // Send a ping afterwards // If we get a response the priority 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 ConnectionShouldCloseAndSignalDoneWhenReadingFromInputFails(bool isServer) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var failableInPipe = new FailingPipe(inPipe); var http2Con = await ConnectionUtils.BuildEstablishedConnection( isServer, failableInPipe, outPipe, loggerProvider); // Make the next write attempt fail failableInPipe.FailNextRead = true; // Send something which triggers no response but will start a new read call await inPipe.WriteWindowUpdate(0, 128); // Wait for the connection to close the outgoing part await outPipe.AssertStreamEnd(); // If the connection was successfully closed close the incoming data // stream, since this is expected from a bidirectional stream implementation await inPipe.CloseAsync(); // Expect the connection to close within timeout var closed = http2Con.Done; Assert.True( closed == await Task.WhenAny(closed, Task.Delay(1000)), "Expected connection to close"); }
public async Task PingShouldFailWhenConnectionIsClosedAfterPingStart() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider); // Request ping var pingTask = http2Con.PingAsync(); // Expect ping emission var fh = await outPipe.ReadFrameHeaderWithTimeout(); Assert.Equal(FrameType.Ping, fh.Type); Assert.Equal(8, fh.Length); Assert.Equal(0, fh.Flags); Assert.Equal(0u, fh.StreamId); var pingData = new ArraySegment <byte>(new byte[8]); await outPipe.ReadAllWithTimeout(pingData); // Close the connection await inPipe.CloseAsync(); await outPipe.AssertStreamEnd(); // Await ping to finish with exception var timeoutTask = Task.Delay(200); Assert.True( pingTask == await Task.WhenAny(pingTask, timeoutTask), "Expected pingTask to finish"); await Assert.ThrowsAsync <ConnectionClosedException>( async() => await pingTask); }
public async Task PingAsyncShouldSendPingAndWaitForAssociatedAck() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider); // Request ping var pingTask = http2Con.PingAsync(); // Expect ping emission var fh = await outPipe.ReadFrameHeaderWithTimeout(); Assert.Equal(FrameType.Ping, fh.Type); Assert.Equal(8, fh.Length); Assert.Equal(0, fh.Flags); Assert.Equal(0u, fh.StreamId); var pingData = new ArraySegment <byte>(new byte[8]); await outPipe.ReadAllWithTimeout(pingData); // Respond with pong fh.Flags = (byte)PingFrameFlags.Ack; await inPipe.WriteFrameHeader(fh); await inPipe.WriteAsync(pingData); // Await ping task to finish Assert.True( pingTask == await Task.WhenAny(pingTask, Task.Delay(200)), "Expected pingTask to finish"); }
public async Task IfSettingsDecreaseHeaderTableNextOutgoingHeadersShouldContainAnUpdate() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); Func <IStream, bool> listener = (s) => { Task.Run(() => { var res = new HeaderField[] { new HeaderField { Name = ":status", Value = "200" }, }; s.WriteHeadersAsync(res, false); }); return(true); }; var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider, listener); // Change remote settings 2 times var settings = Settings.Default; settings.HeaderTableSize = 8; await inPipe.WriteSettings(settings); await outPipe.AssertSettingsAck(); settings.HeaderTableSize = 30; await inPipe.WriteSettings(settings); await outPipe.AssertSettingsAck(); // Establish a stream // When we send a response through it we should observe the size udpate var hEncoder = new Encoder(); await inPipe.WriteHeaders(hEncoder, 1, false, DefaultGetHeaders); // Wait for the incoming status headers with header update var fh = await outPipe.ReadFrameHeaderWithTimeout(); Assert.Equal(FrameType.Headers, fh.Type); Assert.Equal((byte)(HeadersFrameFlags.EndOfHeaders), fh.Flags); Assert.Equal(1u, fh.StreamId); Assert.Equal(3, fh.Length); var data = new byte[fh.Length]; await outPipe.ReadAllWithTimeout(new ArraySegment <byte>(data)); Assert.Equal(0x28, data[0]); // Size Update to 8 Assert.Equal(0x3e, data[1]); // Size Update to 30 Assert.Equal(0x88, data[2]); // :status 200 }
public async Task CreatingStreamsOnServerConnectionShouldYieldException() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var conn = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider); var ex = await Assert.ThrowsAsync <NotSupportedException>( () => conn.CreateStreamAsync(DefaultGetHeaders)); Assert.Equal("Streams can only be created for clients", ex.Message); }
public async Task ResetsOnIdleStreamsShouldBeTreatedAsConnectionError( bool isServer, uint streamId) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( isServer, inPipe, outPipe, loggerProvider); await inPipe.WriteResetStream(streamId, ErrorCode.RefusedStream); await outPipe.AssertGoAwayReception(ErrorCode.ProtocolError, 0u); }
public async Task ADataFrameOnAnIdleStreamIdShouldTriggerAGoAway( 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); await inPipe.WriteData(streamId, 0); await outPipe.AssertGoAwayReception(ErrorCode.StreamClosed, 0u); }
public async Task ConnectionShouldGoAwayOnInvalidResetStreamId( bool isServer) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( isServer, inPipe, outPipe, loggerProvider); await inPipe.WriteResetStream(0, ErrorCode.Cancel); await outPipe.AssertGoAwayReception(ErrorCode.ProtocolError, 0); await outPipe.AssertStreamEnd(); }
public async Task ConnectionShouldGoAwayOnPushPromiseWithInvalidLength( bool isServer, int?padLen) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( isServer, inPipe, outPipe, loggerProvider); // This test is set up in a way where the payload length is 1 byte // too small for the transferred content var requiredLen = 4; var flags = (byte)PushPromiseFrameFlags.EndOfHeaders; if (padLen != null) { flags |= (byte)PushPromiseFrameFlags.Padded; requiredLen += 1 + padLen.Value; } var actualLen = requiredLen - 1; var fh = new FrameHeader { Type = FrameType.PushPromise, Flags = flags, Length = actualLen, StreamId = 1, }; await inPipe.WriteFrameHeader(fh); var content = new byte[actualLen]; var offset = 0; if (padLen != null) { content[offset] = (byte)padLen.Value; offset++; } // Set promised stream Id content[offset + 0] = content[offset + 1] = content[offset + 2] = 0; if (offset + 3 <= content.Length - 1) { content[offset + 3] = 1; } offset += 4; await inPipe.WriteAsync(new ArraySegment <byte>(content)); await outPipe.AssertGoAwayReception(ErrorCode.ProtocolError, 0); await outPipe.AssertStreamEnd(); }
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 ShouldGoAwayWhenConnectionFlowControlWindowIsOverloaded( int amount) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider, null); await inPipe.WriteWindowUpdate(0, amount); await outPipe.AssertGoAwayReception(ErrorCode.FlowControlError, 0); await outPipe.AssertStreamEnd(); }
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 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 PingAsyncShouldNotCompleteWhenNoPongIsSend() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider); // Request ping var pingTask = http2Con.PingAsync(); // Await ping not to finish var timeoutTask = Task.Delay(200); Assert.True( timeoutTask == await Task.WhenAny(pingTask, timeoutTask), "Expected pingTask not to finish"); }
public async Task ContinuationsWithoutHeadersShouldLeadToGoAway() { 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.WriteContinuation(hEncoder, 1u, DefaultGetHeaders, true); await outPipe.AssertGoAwayReception(ErrorCode.ProtocolError, 0); await outPipe.AssertStreamEnd(); }
public async Task ConnectionShouldSignalDoneWhenInputIsClosed(bool isServer) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( isServer, inPipe, outPipe, loggerProvider); await inPipe.CloseAsync(); // Expect the connection to close within timeout var closed = http2Con.Done; Assert.True( closed == await Task.WhenAny(closed, Task.Delay(1000)), "Expected connection to close"); }
public async Task HeadersOnStreamId0ShouldTriggerAGoAway( bool isServer) { 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, 0, false, DefaultGetHeaders); await outPipe.AssertGoAwayReception(ErrorCode.ProtocolError, 0); await outPipe.AssertStreamEnd(); }
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 ReceivingHeadersShouldCreateNewStreamsAndAllowToReadTheHeaders() { const int nrStreams = 10; var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); int nrAcceptedStreams = 0; var handlerDone = new SemaphoreSlim(0); uint streamId = 1; var headersOk = false; var streamIdOk = false; var streamStateOk = false; Func<IStream, bool> listener = (s) => { Interlocked.Increment(ref nrAcceptedStreams); Task.Run(async () => { var rcvdHeaders = await s.ReadHeadersAsync(); headersOk = DefaultGetHeaders.SequenceEqual(rcvdHeaders); streamIdOk = s.Id == streamId; streamStateOk = s.State == StreamState.Open; handlerDone.Release(); }); return true; }; var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider, listener); var hEncoder = new Encoder(); for (var i = 0; i < nrStreams; i++) { headersOk = false; streamIdOk = false; streamStateOk = false; await inPipe.WriteHeaders(hEncoder, streamId, false, DefaultGetHeaders); var requestDone = await handlerDone.WaitAsync(ReadableStreamTestExtensions.ReadTimeout); Assert.True(requestDone, "Expected handler to complete within timeout"); Assert.True(headersOk); Assert.True(streamIdOk); Assert.True(streamStateOk); streamId += 2; } Assert.Equal(nrStreams, nrAcceptedStreams); }
public async Task ConnectionShouldGoAwayOnPriorityStreamIdZero() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider); var prioData = new PriorityData { StreamDependency = 1, StreamDependencyIsExclusive = false, Weight = 0, }; await inPipe.WritePriority(0, prioData); await outPipe.AssertGoAwayReception(ErrorCode.ProtocolError, 0u); await outPipe.AssertStreamEnd(); }
public async Task CreatingStreamShouldEmitHeaders() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var conn = await ConnectionUtils.BuildEstablishedConnection( false, inPipe, outPipe, loggerProvider); var stream1Task = conn.CreateStreamAsync(DefaultGetHeaders, false); var stream1 = await stream1Task; Assert.Equal(StreamState.Open, stream1.State); Assert.Equal(1u, stream1.Id); var fh = await outPipe.ReadFrameHeaderWithTimeout(); Assert.Equal(1u, fh.StreamId); Assert.Equal(FrameType.Headers, fh.Type); Assert.Equal((byte)HeadersFrameFlags.EndOfHeaders, fh.Flags); Assert.Equal(EncodedDefaultGetHeaders.Length, fh.Length); var hdrData = new byte[fh.Length]; await outPipe.ReadWithTimeout(new ArraySegment <byte>(hdrData)); Assert.Equal(EncodedDefaultGetHeaders, hdrData); var stream3Task = conn.CreateStreamAsync(DefaultGetHeaders, true); var stream3 = await stream3Task; Assert.Equal(StreamState.HalfClosedLocal, stream3.State); Assert.Equal(3u, stream3.Id); fh = await outPipe.ReadFrameHeaderWithTimeout(); Assert.Equal(3u, fh.StreamId); Assert.Equal(FrameType.Headers, fh.Type); Assert.Equal( (byte)(HeadersFrameFlags.EndOfHeaders | HeadersFrameFlags.EndOfStream), fh.Flags); Assert.Equal(EncodedIndexedDefaultGetHeaders.Length, fh.Length); var hdrData3 = new byte[fh.Length]; await outPipe.ReadWithTimeout(new ArraySegment <byte>(hdrData3)); Assert.Equal(EncodedIndexedDefaultGetHeaders, hdrData3); }