예제 #1
0
        public async Task CopyToAsyncThrowsOnTimeout()
        {
            using (var input = new TestInput())
            {
                var mockTimeoutControl = new Mock <ITimeoutControl>();

                input.FrameContext.TimeoutControl = mockTimeoutControl.Object;

                var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders {
                    HeaderContentLength = "5"
                }, input.Frame);

                // Add some input and read it to start PumpAsync
                input.Add("a");
                Assert.Equal(1, await body.ReadAsync(new ArraySegment <byte>(new byte[1])));

                // Time out on the next read
                mockTimeoutControl
                .Setup(timeoutControl => timeoutControl.TimedOut)
                .Returns(true);

                input.Cancel();

                using (var ms = new MemoryStream())
                {
                    var exception = await Assert.ThrowsAsync <BadHttpRequestException>(() => body.CopyToAsync(ms));

                    Assert.Equal(StatusCodes.Status408RequestTimeout, exception.StatusCode);
                }

                await body.StopAsync();
            }
        }
예제 #2
0
        public async Task CanHandleLargeBlocks()
        {
            using (var input = new TestInput())
            {
                var body = MessageBody.For(HttpVersion.Http10, new FrameRequestHeaders {
                    HeaderContentLength = "8197"
                }, input.Frame);
                var stream = new FrameRequestStream(Mock.Of <IHttpBodyControlFeature>());
                stream.StartAcceptingReads(body);

                // Input needs to be greater than 4032 bytes to allocate a block not backed by a slab.
                var largeInput = new string('a', 8192);

                input.Add(largeInput);
                // Add a smaller block to the end so that SocketInput attempts to return the large
                // block to the memory pool.
                input.Add("Hello");

                var ms = new MemoryStream();

                await stream.CopyToAsync(ms);

                var requestArray = ms.ToArray();
                Assert.Equal(8197, requestArray.Length);
                AssertASCII(largeInput + "Hello", new ArraySegment <byte>(requestArray, 0, requestArray.Length));

                await body.StopAsync();
            }
        }
예제 #3
0
        public async Task LogsWhenStopsReadingRequestBody()
        {
            using (var input = new TestInput())
            {
                var logEvent   = new ManualResetEventSlim();
                var mockLogger = new Mock <IKestrelTrace>();
                mockLogger
                .Setup(logger => logger.RequestBodyDone("ConnectionId", "RequestId"))
                .Callback(() => logEvent.Set());
                input.Frame.ServiceContext.Log  = mockLogger.Object;
                input.Frame.ConnectionIdFeature = "ConnectionId";
                input.Frame.TraceIdentifier     = "RequestId";

                var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders {
                    HeaderContentLength = "2"
                }, input.Frame);
                var stream = new FrameRequestStream(Mock.Of <IHttpBodyControlFeature>());
                stream.StartAcceptingReads(body);

                // Add some input and consume it to ensure PumpAsync is running
                input.Add("a");
                Assert.Equal(1, await stream.ReadAsync(new byte[1], 0, 1));

                input.Fin();

                Assert.True(logEvent.Wait(TimeSpan.FromSeconds(10)));

                await body.StopAsync();
            }
        }
예제 #4
0
        public async Task OnlyEnforcesRequestBodyTimeoutAfterSending100Continue()
        {
            using (var input = new TestInput())
            {
                var produceContinueCalled = false;
                var startTimingReadsCalledAfterProduceContinue = false;

                var mockFrameControl = new Mock <IFrameControl>();
                mockFrameControl
                .Setup(frameControl => frameControl.ProduceContinue())
                .Callback(() => produceContinueCalled = true);
                input.Frame.FrameControl = mockFrameControl.Object;

                var mockTimeoutControl = new Mock <ITimeoutControl>();
                mockTimeoutControl
                .Setup(timeoutControl => timeoutControl.StartTimingReads())
                .Callback(() => startTimingReadsCalledAfterProduceContinue = produceContinueCalled);

                input.FrameContext.TimeoutControl = mockTimeoutControl.Object;

                var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders {
                    HeaderContentLength = "5"
                }, input.Frame);

                // Add some input and read it to start PumpAsync
                var readTask = body.ReadAsync(new ArraySegment <byte>(new byte[1]));

                Assert.True(startTimingReadsCalledAfterProduceContinue);

                input.Add("a");
                await readTask;

                await body.StopAsync();
            }
        }
예제 #5
0
        public async Task DoesNotEnforceRequestBodyTimeoutOnUpgradeRequests()
        {
            using (var input = new TestInput())
            {
                var mockTimeoutControl = new Mock <ITimeoutControl>();
                input.FrameContext.TimeoutControl = mockTimeoutControl.Object;

                var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders {
                    HeaderConnection = "upgrade"
                }, input.Frame);

                // Add some input and read it to start PumpAsync
                input.Add("a");
                Assert.Equal(1, await body.ReadAsync(new ArraySegment <byte>(new byte[1])));

                input.Fin();

                await Assert.ThrowsAsync <BadHttpRequestException>(async() => await body.ReadAsync(new ArraySegment <byte>(new byte[1])));

                mockTimeoutControl.Verify(timeoutControl => timeoutControl.StartTimingReads(), Times.Never);
                mockTimeoutControl.Verify(timeoutControl => timeoutControl.StopTimingReads(), Times.Never);

                // Due to the limits set on Frame.RequestBodyPipe, backpressure should be triggered on every
                // write to that pipe. Verify that read timing pause and resume are not called on upgrade
                // requests.
                mockTimeoutControl.Verify(timeoutControl => timeoutControl.PauseTimingReads(), Times.Never);
                mockTimeoutControl.Verify(timeoutControl => timeoutControl.ResumeTimingReads(), Times.Never);

                await body.StopAsync();
            }
        }
예제 #6
0
        public async Task CanReadFromRemainingData(HttpVersion httpVersion)
        {
            using (var input = new TestInput())
            {
                var body = MessageBody.For(httpVersion, new FrameRequestHeaders {
                    HeaderConnection = "upgrade"
                }, input.Frame);
                var mockBodyControl = new Mock <IHttpBodyControlFeature>();
                mockBodyControl.Setup(m => m.AllowSynchronousIO).Returns(true);
                var stream = new FrameRequestStream(mockBodyControl.Object);
                stream.StartAcceptingReads(body);

                input.Add("Hello");

                var buffer = new byte[1024];

                var count = stream.Read(buffer, 0, buffer.Length);
                Assert.Equal(5, count);
                AssertASCII("Hello", new ArraySegment <byte>(buffer, 0, count));

                input.Fin();

                await body.StopAsync();
            }
        }
예제 #7
0
        public async Task ReadExitsGivenIncompleteChunkedExtension()
        {
            using (var input = new TestInput())
            {
                var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders {
                    HeaderTransferEncoding = "chunked"
                }, input.Frame);
                var stream = new FrameRequestStream(Mock.Of <IHttpBodyControlFeature>());
                stream.StartAcceptingReads(body);

                input.Add("5;\r\0");

                var buffer   = new byte[1024];
                var readTask = stream.ReadAsync(buffer, 0, buffer.Length);

                Assert.False(readTask.IsCompleted);

                input.Add("\r\r\r\nHello\r\n0\r\n\r\n");

                Assert.Equal(5, await readTask.TimeoutAfter(TimeSpan.FromSeconds(10)));
                Assert.Equal(0, await stream.ReadAsync(buffer, 0, buffer.Length));

                await body.StopAsync();
            }
        }
예제 #8
0
        public async Task CanHandleLargeBlocks()
        {
            using (var input = new TestInput())
            {
                var body   = MessageBody.For("HTTP/1.0", new FrameRequestHeaders(), input.FrameContext);
                var stream = new FrameRequestStream();
                stream.StartAcceptingReads(body);

                // Input needs to be greater than 4032 bytes to allocate a block not backed by a slab.
                var largeInput = new string('a', 8192);

                input.Add(largeInput, true);
                // Add a smaller block to the end so that SocketInput attempts to return the large
                // block to the memory pool.
                input.Add("Hello", true);

                var readBuffer = new byte[8192];

                var count1 = await stream.ReadAsync(readBuffer, 0, 8192);

                Assert.Equal(8192, count1);
                AssertASCII(largeInput, new ArraySegment <byte>(readBuffer, 0, 8192));

                var count2 = await stream.ReadAsync(readBuffer, 0, 8192);

                Assert.Equal(5, count2);
                AssertASCII("Hello", new ArraySegment <byte>(readBuffer, 0, 5));

                var count3 = await stream.ReadAsync(readBuffer, 0, 8192);

                Assert.Equal(0, count3);
            }
        }
예제 #9
0
        public async Task CanReadFromChunkedEncoding()
        {
            using (var input = new TestInput())
            {
                var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders {
                    HeaderTransferEncoding = "chunked"
                }, input.Frame);
                var mockBodyControl = new Mock <IHttpBodyControlFeature>();
                mockBodyControl.Setup(m => m.AllowSynchronousIO).Returns(true);
                var stream = new FrameRequestStream(mockBodyControl.Object);
                stream.StartAcceptingReads(body);

                input.Add("5\r\nHello\r\n");

                var buffer = new byte[1024];

                var count = stream.Read(buffer, 0, buffer.Length);
                Assert.Equal(5, count);
                AssertASCII("Hello", new ArraySegment <byte>(buffer, 0, count));

                input.Add("0\r\n\r\n");

                count = stream.Read(buffer, 0, buffer.Length);
                Assert.Equal(0, count);

                await body.StopAsync();
            }
        }
예제 #10
0
        public async Task CanReadAsyncFromContentLength(HttpVersion httpVersion)
        {
            using (var input = new TestInput())
            {
                var body = MessageBody.For(httpVersion, new FrameRequestHeaders {
                    HeaderContentLength = "5"
                }, input.Frame);
                var stream = new FrameRequestStream(Mock.Of <IHttpBodyControlFeature>());
                stream.StartAcceptingReads(body);

                input.Add("Hello");

                var buffer = new byte[1024];

                var count = await stream.ReadAsync(buffer, 0, buffer.Length);

                Assert.Equal(5, count);
                AssertASCII("Hello", new ArraySegment <byte>(buffer, 0, count));

                count = await stream.ReadAsync(buffer, 0, buffer.Length);

                Assert.Equal(0, count);

                await body.StopAsync();
            }
        }
예제 #11
0
        public void InitializeStreamsResetsStreams()
        {
            // Arrange
            var connectionContext = new ConnectionContext()
            {
                DateHeaderValueManager = new DateHeaderValueManager(),
                ServerAddress          = ServerAddress.FromUrl("http://localhost:5000"),
                ServerOptions          = new KestrelServerOptions(),
                SocketOutput           = new MockSocketOuptut()
            };
            var frame = new Frame <object>(application: null, context: connectionContext);

            frame.InitializeHeaders();

            var messageBody = MessageBody.For("HTTP/1.1", (FrameRequestHeaders)frame.RequestHeaders, frame);

            frame.InitializeStreams(messageBody);

            var originalRequestBody  = frame.RequestBody;
            var originalResponseBody = frame.ResponseBody;
            var originalDuplexStream = frame.DuplexStream;

            frame.RequestBody  = new MemoryStream();
            frame.ResponseBody = new MemoryStream();
            frame.DuplexStream = new MemoryStream();

            // Act
            frame.InitializeStreams(messageBody);

            // Assert
            Assert.Same(originalRequestBody, frame.RequestBody);
            Assert.Same(originalResponseBody, frame.ResponseBody);
            Assert.Same(originalDuplexStream, frame.DuplexStream);
        }
예제 #12
0
        public async Task CopyToAsyncDoesNotCopyBlocks(FrameRequestHeaders headers, string[] data)
        {
            var writeCount      = 0;
            var writeTcs        = new TaskCompletionSource <byte[]>();
            var mockDestination = new Mock <Stream>();

            mockDestination
            .Setup(m => m.WriteAsync(It.IsAny <byte[]>(), It.IsAny <int>(), It.IsAny <int>(), CancellationToken.None))
            .Callback((byte[] buffer, int offset, int count, CancellationToken cancellationToken) =>
            {
                writeTcs.SetResult(buffer);
                writeCount++;
            })
            .Returns(Task.CompletedTask);

            using (var input = new TestInput())
            {
                var body = MessageBody.For(HttpVersion.Http11, headers, input.Frame);

                var copyToAsyncTask = body.CopyToAsync(mockDestination.Object);

                // The block returned by IncomingStart always has at least 2048 available bytes,
                // so no need to bounds check in this test.
                var bytes  = Encoding.ASCII.GetBytes(data[0]);
                var buffer = input.Pipe.Writer.Alloc(2048);
                ArraySegment <byte> block;
                Assert.True(buffer.Buffer.TryGetArray(out block));
                Buffer.BlockCopy(bytes, 0, block.Array, block.Offset, bytes.Length);
                buffer.Advance(bytes.Length);
                await buffer.FlushAsync();

                // Verify the block passed to WriteAsync is the same one incoming data was written into.
                Assert.Same(block.Array, await writeTcs.Task);

                writeTcs = new TaskCompletionSource <byte[]>();
                bytes    = Encoding.ASCII.GetBytes(data[1]);
                buffer   = input.Pipe.Writer.Alloc(2048);
                Assert.True(buffer.Buffer.TryGetArray(out block));
                Buffer.BlockCopy(bytes, 0, block.Array, block.Offset, bytes.Length);
                buffer.Advance(bytes.Length);
                await buffer.FlushAsync();

                Assert.Same(block.Array, await writeTcs.Task);

                if (headers.HeaderConnection == "close")
                {
                    input.Pipe.Writer.Complete();
                }

                await copyToAsyncTask;

                Assert.Equal(2, writeCount);

                await body.StopAsync();
            }
        }
예제 #13
0
        public void ForThrowsWhenMethodRequiresLengthButNoContentLengthSetHttp10(string method)
        {
            using (var input = new TestInput())
            {
                input.Frame.Method = method;
                var ex = Assert.Throws <BadHttpRequestException>(() =>
                                                                 MessageBody.For(HttpVersion.Http10, new FrameRequestHeaders(), input.Frame));

                Assert.Equal(StatusCodes.Status400BadRequest, ex.StatusCode);
                Assert.Equal(CoreStrings.FormatBadRequest_LengthRequiredHttp10(method), ex.Message);
            }
        }
예제 #14
0
        public void ForThrowsWhenFinalTransferCodingIsNotChunked()
        {
            using (var input = new TestInput())
            {
                var ex = Assert.Throws <BadHttpRequestException>(() =>
                                                                 MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders {
                    HeaderTransferEncoding = "chunked, not-chunked"
                }, input.Frame));

                Assert.Equal(StatusCodes.Status400BadRequest, ex.StatusCode);
                Assert.Equal(CoreStrings.FormatBadRequest_FinalTransferCodingNotChunked("chunked, not-chunked"), ex.Message);
            }
        }
예제 #15
0
        public async Task ReadAsyncFromNoContentLengthReturnsZero(HttpVersion httpVersion)
        {
            using (var input = new TestInput())
            {
                var body   = MessageBody.For(httpVersion, new FrameRequestHeaders(), input.Frame);
                var stream = new FrameRequestStream(Mock.Of <IHttpBodyControlFeature>());
                stream.StartAcceptingReads(body);

                input.Add("Hello");

                var buffer = new byte[1024];
                Assert.Equal(0, await stream.ReadAsync(buffer, 0, buffer.Length));

                await body.StopAsync();
            }
        }
예제 #16
0
        public async Task ConsumeAsyncConsumesAllRemainingInput()
        {
            using (var input = new TestInput())
            {
                var body = MessageBody.For(HttpVersion.Http10, new FrameRequestHeaders {
                    HeaderContentLength = "5"
                }, input.Frame);

                input.Add("Hello");

                await body.ConsumeAsync();

                Assert.Equal(0, await body.ReadAsync(new ArraySegment <byte>(new byte[1])));

                await body.StopAsync();
            }
        }
        public void Http10ConnectionClose()
        {
            var input  = new TestInput();
            var body   = MessageBody.For("HTTP/1.0", new Dictionary <string, StringValues>(), input.FrameContext);
            var stream = new FrameRequestStream(body);

            input.Add("Hello", true);

            var buffer1 = new byte[1024];
            var count1  = stream.Read(buffer1, 0, 1024);

            AssertASCII("Hello", new ArraySegment <byte>(buffer1, 0, 5));

            var buffer2 = new byte[1024];
            var count2  = stream.Read(buffer2, 0, 1024);

            Assert.Equal(0, count2);
        }
예제 #18
0
        public async Task ReadFromNoContentLengthReturnsZero(HttpVersion httpVersion)
        {
            using (var input = new TestInput())
            {
                var body            = MessageBody.For(httpVersion, new FrameRequestHeaders(), input.Frame);
                var mockBodyControl = new Mock <IHttpBodyControlFeature>();
                mockBodyControl.Setup(m => m.AllowSynchronousIO).Returns(true);
                var stream = new FrameRequestStream(mockBodyControl.Object);
                stream.StartAcceptingReads(body);

                input.Add("Hello");

                var buffer = new byte[1024];
                Assert.Equal(0, stream.Read(buffer, 0, buffer.Length));

                await body.StopAsync();
            }
        }
        public async Task Http10ConnectionCloseAsync()
        {
            var input  = new TestInput();
            var body   = MessageBody.For("HTTP/1.0", new FrameRequestHeaders(), input.FrameContext);
            var stream = new FrameRequestStream().StartAcceptingReads(body);

            input.Add("Hello", true);

            var buffer1 = new byte[1024];
            var count1  = await stream.ReadAsync(buffer1, 0, 1024);

            AssertASCII("Hello", new ArraySegment <byte>(buffer1, 0, 5));

            var buffer2 = new byte[1024];
            var count2  = await stream.ReadAsync(buffer2, 0, 1024);

            Assert.Equal(0, count2);
        }
예제 #20
0
        public async Task CopyToAsyncDoesNotCompletePipeReader()
        {
            using (var input = new TestInput())
            {
                var body = MessageBody.For(HttpVersion.Http10, new FrameRequestHeaders {
                    HeaderContentLength = "5"
                }, input.Frame);

                input.Add("Hello");

                using (var ms = new MemoryStream())
                {
                    await body.CopyToAsync(ms);
                }

                Assert.Equal(0, await body.ReadAsync(new ArraySegment <byte>(new byte[1])));

                await body.StopAsync();
            }
        }
예제 #21
0
        public void InitializeStreamsResetsStreams()
        {
            // Arrange
            var messageBody = MessageBody.For(Kestrel.Core.Internal.Http.HttpVersion.Http11, (FrameRequestHeaders)_frame.RequestHeaders, _frame);

            _frame.InitializeStreams(messageBody);

            var originalRequestBody  = _frame.RequestBody;
            var originalResponseBody = _frame.ResponseBody;

            _frame.RequestBody  = new MemoryStream();
            _frame.ResponseBody = new MemoryStream();

            // Act
            _frame.InitializeStreams(messageBody);

            // Assert
            Assert.Same(originalRequestBody, _frame.RequestBody);
            Assert.Same(originalResponseBody, _frame.ResponseBody);
        }
예제 #22
0
        public async Task ReadThrowsGivenChunkPrefixGreaterThan8Bytes()
        {
            using (var input = new TestInput())
            {
                var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders {
                    HeaderTransferEncoding = "chunked"
                }, input.Frame);
                var stream = new FrameRequestStream(Mock.Of <IHttpBodyControlFeature>());
                stream.StartAcceptingReads(body);

                input.Add("012345678\r");

                var buffer = new byte[1024];
                var ex     = await Assert.ThrowsAsync <BadHttpRequestException>(async() =>
                                                                                await stream.ReadAsync(buffer, 0, buffer.Length));

                Assert.Equal(CoreStrings.BadRequest_BadChunkSizeData, ex.Message);

                await body.StopAsync();
            }
        }
예제 #23
0
        public async Task ConnectionUpgradeKeepAlive(string headerConnection)
        {
            using (var input = new TestInput())
            {
                var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders {
                    HeaderConnection = headerConnection
                }, input.Frame);
                var stream = new FrameRequestStream(Mock.Of <IHttpBodyControlFeature>());
                stream.StartAcceptingReads(body);

                input.Add("Hello");

                var buffer = new byte[1024];
                Assert.Equal(5, await stream.ReadAsync(buffer, 0, buffer.Length));
                AssertASCII("Hello", new ArraySegment <byte>(buffer, 0, 5));

                input.Fin();

                await body.StopAsync();
            }
        }
예제 #24
0
        public async Task PausesAndResumesRequestBodyTimeoutOnBackpressure()
        {
            using (var input = new TestInput())
            {
                var mockTimeoutControl = new Mock <ITimeoutControl>();
                input.FrameContext.TimeoutControl = mockTimeoutControl.Object;

                var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders {
                    HeaderContentLength = "12"
                }, input.Frame);

                // Add some input and read it to start PumpAsync
                input.Add("hello,");
                Assert.Equal(6, await body.ReadAsync(new ArraySegment <byte>(new byte[6])));

                input.Add(" world");
                Assert.Equal(6, await body.ReadAsync(new ArraySegment <byte>(new byte[6])));

                // Due to the limits set on Frame.RequestBodyPipe, backpressure should be triggered on every write to that pipe.
                mockTimeoutControl.Verify(timeoutControl => timeoutControl.PauseTimingReads(), Times.Exactly(2));
                mockTimeoutControl.Verify(timeoutControl => timeoutControl.ResumeTimingReads(), Times.Exactly(2));
            }
        }
예제 #25
0
        public async Task PumpAsyncDoesNotReturnAfterCancelingInput()
        {
            using (var input = new TestInput())
            {
                var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders {
                    HeaderContentLength = "2"
                }, input.Frame);
                var stream = new FrameRequestStream(Mock.Of <IHttpBodyControlFeature>());
                stream.StartAcceptingReads(body);

                // Add some input and consume it to ensure PumpAsync is running
                input.Add("a");
                Assert.Equal(1, await stream.ReadAsync(new byte[1], 0, 1));

                input.Pipe.Reader.CancelPendingRead();

                // Add more input and verify is read
                input.Add("b");
                Assert.Equal(1, await stream.ReadAsync(new byte[1], 0, 1));

                await body.StopAsync();
            }
        }
예제 #26
0
        public async Task StopAsyncPreventsFurtherDataConsumption()
        {
            using (var input = new TestInput())
            {
                var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders {
                    HeaderContentLength = "2"
                }, input.Frame);
                var stream = new FrameRequestStream(Mock.Of <IHttpBodyControlFeature>());
                stream.StartAcceptingReads(body);

                // Add some input and consume it to ensure PumpAsync is running
                input.Add("a");
                Assert.Equal(1, await stream.ReadAsync(new byte[1], 0, 1));

                await body.StopAsync();

                // Add some more data. Checking for cancelation and exiting the loop
                // should take priority over reading this data.
                input.Add("b");

                // There shouldn't be any additional data available
                Assert.Equal(0, await stream.ReadAsync(new byte[1], 0, 1));
            }
        }