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(); } }
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(); } }
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(); } }
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(); } }
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(); } }
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(); } }
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(); } }
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); } }
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(); } }
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(); } }
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); }
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(); } }
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); } }
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); } }
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(); } }
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); }
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); }
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(); } }
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); }
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(); } }
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(); } }
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)); } }
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(); } }
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)); } }