public void CreatingAConnectionWithInvalidUpgradeShouldThrow() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var config = new ConnectionConfigurationBuilder(true) .UseStreamListener(s => false) .Build(); var builder = new ServerUpgradeRequestBuilder(); builder.SetHeaders(DefaultGetHeaders.ToList()); builder.SetHttp2Settings("!"); var upgrade = builder.Build(); Assert.False(upgrade.IsValid); var ex = Assert.Throws <ArgumentException>(() => { var conn = new Connection( config, inPipe, outPipe, new Connection.Options { Logger = loggerProvider.CreateLogger(""), ServerUpgradeRequest = upgrade, }); }); Assert.Equal( "The ServerUpgradeRequest is invalid.\n" + "Invalid upgrade requests must be denied by the HTTP/1 handler", ex.Message); }
public void UpgradesWithPayloadMustHaveMatchingContentLength( int?contentLength, int?payloadLength, bool isOk) { var builder = new ServerUpgradeRequestBuilder(); builder.SetHttp2Settings(""); var headers = DefaultGetHeaders.ToList(); if (contentLength != null) { headers.Add(new HeaderField() { Name = "content-length", Value = contentLength.ToString() }); } builder.SetHeaders(headers); if (payloadLength != null) { const int padding = 3; var pl = new byte[padding + payloadLength.Value]; builder.SetPayload(new ArraySegment <byte>(pl, padding, payloadLength.Value)); } var upgrade = builder.Build(); Assert.Equal(isOk, upgrade.IsValid); }
public void UpgradesWithInvalidEncodedHttp2SettingsStringShouldBeInvalid( string encodedSettings) { var builder = new ServerUpgradeRequestBuilder(); builder.SetHeaders(DefaultGetHeaders.ToList()); builder.SetHttp2Settings(encodedSettings); var upgrade = builder.Build(); Assert.False(upgrade.IsValid); }
public void ValidHttp2SettingsShouldBeAccepted( byte[] payload) { var builder = new ServerUpgradeRequestBuilder(); builder.SetHeaders(DefaultGetHeaders.ToList()); var base64 = Convert.ToBase64String(payload); base64 = base64.Replace('/', '_'); base64 = base64.Replace('+', '-'); builder.SetHttp2Settings(base64); var upgrade = builder.Build(); Assert.True(upgrade.IsValid); }
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 InvalidContinuationFramesShouldLeadToGoAway( uint contStreamId, int?contLength, FrameType contFrameType) { 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(); // Send a valid HEADERS frame await inPipe.WriteHeaders( hEncoder, 1, false, DefaultGetHeaders.Take(2), false); // Followed by an invalid continuation frame var outBuf = new byte[Settings.Default.MaxFrameSize]; var result = hEncoder.EncodeInto( new ArraySegment <byte>(outBuf), DefaultGetHeaders.Skip(2)); var length = contLength ?? result.UsedBytes; var fh = new FrameHeader { Type = contFrameType, StreamId = contStreamId, Length = length, Flags = 0, }; await inPipe.WriteFrameHeader(fh); await outPipe.AssertGoAwayReception(ErrorCode.ProtocolError, 0u); await outPipe.AssertStreamEnd(); }
public async Task ReceivingHeadersWithPaddingAndPriorityShouldBeSupported( int?numPadding, bool hasPrio) { 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(); var outBuf = new byte[Settings.Default.MaxFrameSize]; for (var i = 0; i < nrStreams; i++) { headersOk = false; streamIdOk = false; streamStateOk = false; var headerOffset = 0; if (numPadding != null) { outBuf[0] = (byte)numPadding; headerOffset += 1; } if (hasPrio) { // TODO: Initialize the priority data in this test properly // if priority data checking gets inserted later on headerOffset += 5; } var result = hEncoder.EncodeInto( new ArraySegment <byte>(outBuf, headerOffset, outBuf.Length - headerOffset), DefaultGetHeaders); var totalLength = headerOffset + result.UsedBytes; if (numPadding != null) { totalLength += numPadding.Value; } var flags = (byte)HeadersFrameFlags.EndOfHeaders; if (numPadding != null) { flags |= (byte)HeadersFrameFlags.Padded; } if (hasPrio) { flags |= (byte)HeadersFrameFlags.Priority; } var fh = new FrameHeader { Type = FrameType.Headers, Length = totalLength, Flags = (byte)flags, StreamId = streamId, }; await inPipe.WriteFrameHeader(fh); await inPipe.WriteAsync(new ArraySegment <byte>(outBuf, 0, totalLength)); 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 ServerUpgradeRequestsShouldDispatchStream1( int payloadLength) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); int nrAcceptedStreams = 0; IStream stream = null; var handlerDone = new SemaphoreSlim(0); Func <IStream, bool> listener = (s) => { Interlocked.Increment(ref nrAcceptedStreams); Task.Run(() => { stream = s; handlerDone.Release(); }); return(true); }; var startOfPayload = 44; byte[] payload = new byte[payloadLength]; for (var i = 0; i < payloadLength; i++) { payload[i] = (byte)(startOfPayload + i); } var builder = new ServerUpgradeRequestBuilder(); var headers = DefaultGetHeaders.ToList(); if (payloadLength != 0) { builder.SetPayload(new ArraySegment <byte>(payload)); headers.Add(new HeaderField() { Name = "content-length", Value = payloadLength.ToString() }); } builder.SetHeaders(headers); builder.SetHttp2Settings(""); var upgrade = builder.Build(); var config = new ConnectionConfigurationBuilder(true) .UseStreamListener(listener) .Build(); var conn = new Connection( config, inPipe, outPipe, new Connection.Options { Logger = loggerProvider.CreateLogger("http2Con"), ServerUpgradeRequest = upgrade, }); await conn.PerformHandshakes(inPipe, outPipe); var requestDone = await handlerDone.WaitAsync( ReadableStreamTestExtensions.ReadTimeout); Assert.True(requestDone, "Expected handler to complete within timeout"); Assert.Equal(1u, stream.Id); Assert.Equal(StreamState.HalfClosedRemote, stream.State); var rcvdHeaders = await stream.ReadHeadersAsync(); Assert.True(headers.SequenceEqual(rcvdHeaders)); var allData = await stream.ReadAllToArrayWithTimeout(); Assert.Equal(payloadLength, allData.Length); Assert.Equal(payload, allData); Assert.Equal(1, nrAcceptedStreams); }
public async Task IncompleteHeaderBlocksShouldBeDetected( int nrContinuations) { 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(); // Construct a proper set of header here, where a single headerfield could // by splitted over more than 1 fragment var totalHeaders = DefaultGetHeaders.TakeWhile( h => h.Name.StartsWith(":")); totalHeaders = totalHeaders.Append( new HeaderField { Name = "longname", Value = "longvalue" }); totalHeaders = totalHeaders.Append( new HeaderField { Name = "abc", Value = "xyz" }); // Encode all headers in a single header block var hBuf = new byte[32 * 1024]; var encodeRes = hEncoder.EncodeInto(new ArraySegment <byte>(hBuf), totalHeaders); Assert.Equal(totalHeaders.Count(), encodeRes.FieldCount); Assert.True( encodeRes.UsedBytes >= 5, "Test must encode headers with at least 5 bytes to work properly"); // Write header data in multiple parts // The last part will miss one byte var offset = 0; var length = encodeRes.UsedBytes; var isLast = nrContinuations == 0; var dataLength = isLast ? (length - 1) : 3; var fh = new FrameHeader { Type = FrameType.Headers, Length = dataLength, StreamId = 1u, Flags = (byte)(isLast ? HeadersFrameFlags.EndOfHeaders : 0), }; await inPipe.WriteFrameHeader(fh); await inPipe.WriteAsync(new ArraySegment <byte>(hBuf, offset, dataLength)); offset += dataLength; length -= dataLength; for (var i = 0; i < nrContinuations; i++) { isLast = i == nrContinuations - 1; dataLength = isLast ? (length - 1) : 1; fh = new FrameHeader { Type = FrameType.Continuation, Length = dataLength, StreamId = 1u, Flags = (byte)(isLast ? ContinuationFrameFlags.EndOfHeaders : 0), }; await inPipe.WriteFrameHeader(fh); await inPipe.WriteAsync(new ArraySegment <byte>(hBuf, offset, dataLength)); offset += dataLength; length -= dataLength; } Assert.True(1 == length, "Expected to send all but 1 byte"); // Expect a GoAway as reaction to incomplete headers await outPipe.AssertGoAwayReception(ErrorCode.CompressionError, 0u); await outPipe.AssertStreamEnd(); }
public async Task ContinuationsWherePartsDontContainFullHeaderMustBeSupported( int nrContinuations) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); IStream stream = null; IEnumerable <HeaderField> receivedHeaders = null; SemaphoreSlim handlerDone = new SemaphoreSlim(0); Func <IStream, bool> listener = (s) => { stream = s; Task.Run(async() => { receivedHeaders = await s.ReadHeadersAsync(); handlerDone.Release(); }); return(true); }; var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider, listener); var hEncoder = new Encoder(); // Construct a proper set of header here, where a single headerfield could // by splitted over more than 1 fragment var totalHeaders = DefaultGetHeaders.TakeWhile( h => h.Name.StartsWith(":")); totalHeaders = totalHeaders.Append( new HeaderField { Name = "longname", Value = "longvalue" }); totalHeaders = totalHeaders.Append( new HeaderField { Name = "abc", Value = "xyz" }); // Encode all headers in a single header block var hBuf = new byte[32 * 1024]; var encodeRes = hEncoder.EncodeInto(new ArraySegment <byte>(hBuf), totalHeaders); Assert.Equal(totalHeaders.Count(), encodeRes.FieldCount); Assert.True( encodeRes.UsedBytes >= 5, "Test must encode headers with at least 5 bytes to work properly"); // Write the first 3 bytes in a header frame // These cover all pseudoheaders, which wouldn't be affected by // possible bugs anyway var fh = new FrameHeader { Type = FrameType.Headers, Length = 3, StreamId = 1u, Flags = (byte)0, }; await inPipe.WriteFrameHeader(fh); await inPipe.WriteAsync(new ArraySegment <byte>(hBuf, 0, 3)); var offset = 3; var length = encodeRes.UsedBytes - 3; for (var i = 0; i < nrContinuations; i++) { var isLast = i == nrContinuations - 1; var dataLength = isLast ? length : 1; fh = new FrameHeader { Type = FrameType.Continuation, Length = dataLength, StreamId = 1u, Flags = (byte)(isLast ? ContinuationFrameFlags.EndOfHeaders : 0), }; await inPipe.WriteFrameHeader(fh); await inPipe.WriteAsync(new ArraySegment <byte>(hBuf, offset, dataLength)); offset += dataLength; length -= dataLength; } var handlerCompleted = await handlerDone.WaitAsync( ReadableStreamTestExtensions.ReadTimeout); Assert.True(handlerCompleted, "Expected stream handler to complete"); Assert.True( totalHeaders.SequenceEqual(receivedHeaders), "Expected to receive all sent headers"); }