// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipelines;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel;
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Internal;
using Moq;
using Xunit;
using Xunit.Sdk;
namespace Microsoft.AspNetCore.Server.KestrelTests
{
///
/// Summary description for MessageBodyTests
///
public class MessageBodyTests
{
[Fact]
public void Http10ConnectionClose()
{
using (var input = new TestInput())
{
var body = MessageBody.For(HttpVersion.Http10, new FrameRequestHeaders { HeaderContentLength = "5" }, input.FrameContext);
var stream = new FrameRequestStream();
stream.StartAcceptingReads(body);
input.Add("Hello", true);
var buffer1 = new byte[1024];
var count1 = stream.Read(buffer1, 0, 1024);
AssertASCII("Hello", new ArraySegment(buffer1, 0, 5));
var buffer2 = new byte[1024];
var count2 = stream.Read(buffer2, 0, 1024);
Assert.Equal(0, count2);
}
}
[Fact]
public async Task Http10ConnectionCloseAsync()
{
using (var input = new TestInput())
{
var body = MessageBody.For(HttpVersion.Http10, new FrameRequestHeaders { HeaderContentLength = "5" }, input.FrameContext);
var stream = new FrameRequestStream();
stream.StartAcceptingReads(body);
input.Add("Hello", true);
var buffer1 = new byte[1024];
var count1 = await stream.ReadAsync(buffer1, 0, 1024);
AssertASCII("Hello", new ArraySegment(buffer1, 0, 5));
var buffer2 = new byte[1024];
var count2 = await stream.ReadAsync(buffer2, 0, 1024);
Assert.Equal(0, count2);
}
}
[Fact]
public void Http10NoContentLength()
{
using (var input = new TestInput())
{
var body = MessageBody.For(HttpVersion.Http10, new FrameRequestHeaders(), input.FrameContext);
var stream = new FrameRequestStream();
stream.StartAcceptingReads(body);
input.Add("Hello", true);
var buffer1 = new byte[1024];
Assert.Equal(0, stream.Read(buffer1, 0, 1024));
}
}
[Fact]
public async Task Http10NoContentLengthAsync()
{
using (var input = new TestInput())
{
var body = MessageBody.For(HttpVersion.Http10, new FrameRequestHeaders(), input.FrameContext);
var stream = new FrameRequestStream();
stream.StartAcceptingReads(body);
input.Add("Hello", true);
var buffer1 = new byte[1024];
Assert.Equal(0, await stream.ReadAsync(buffer1, 0, 1024));
}
}
[Fact]
public void Http11NoContentLength()
{
using (var input = new TestInput())
{
var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders(), input.FrameContext);
var stream = new FrameRequestStream();
stream.StartAcceptingReads(body);
input.Add("Hello", true);
var buffer1 = new byte[1024];
Assert.Equal(0, stream.Read(buffer1, 0, 1024));
}
}
[Fact]
public async Task Http11NoContentLengthAsync()
{
using (var input = new TestInput())
{
var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders(), input.FrameContext);
var stream = new FrameRequestStream();
stream.StartAcceptingReads(body);
input.Add("Hello", true);
var buffer1 = new byte[1024];
Assert.Equal(0, await stream.ReadAsync(buffer1, 0, 1024));
}
}
[Fact]
public void Http11NoContentLengthConnectionClose()
{
using (var input = new TestInput())
{
var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders { HeaderConnection = "close" }, input.FrameContext);
var stream = new FrameRequestStream();
stream.StartAcceptingReads(body);
input.Add("Hello", true);
var buffer1 = new byte[1024];
Assert.Equal(0, stream.Read(buffer1, 0, 1024));
}
}
[Fact]
public async Task Http11NoContentLengthConnectionCloseAsync()
{
using (var input = new TestInput())
{
var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders { HeaderConnection = "close" }, input.FrameContext);
var stream = new FrameRequestStream();
stream.StartAcceptingReads(body);
input.Add("Hello", true);
var buffer1 = new byte[1024];
Assert.Equal(0, await stream.ReadAsync(buffer1, 0, 1024));
}
}
[Fact]
public async Task CanHandleLargeBlocks()
{
using (var input = new TestInput())
{
var body = MessageBody.For(HttpVersion.Http10, new FrameRequestHeaders { HeaderContentLength = "8197" }, 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);
// Add a smaller block to the end so that SocketInput attempts to return the large
// block to the memory pool.
input.Add("Hello", fin: true);
var ms = new MemoryStream();
await stream.CopyToAsync(ms);
var requestArray = ms.ToArray();
Assert.Equal(8197, requestArray.Length);
AssertASCII(largeInput + "Hello", new ArraySegment(requestArray, 0, requestArray.Length));
var count = await stream.ReadAsync(new byte[1], 0, 1);
Assert.Equal(0, count);
}
}
[Fact]
public void ForThrowsWhenFinalTransferCodingIsNotChunked()
{
using (var input = new TestInput())
{
var ex = Assert.Throws(() =>
MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders { HeaderTransferEncoding = "chunked, not-chunked" }, input.FrameContext));
Assert.Equal(StatusCodes.Status400BadRequest, ex.StatusCode);
Assert.Equal("Final transfer coding is not \"chunked\": \"chunked, not-chunked\"", ex.Message);
}
}
[Theory]
[InlineData("POST")]
[InlineData("PUT")]
public void ForThrowsWhenMethodRequiresLengthButNoContentLengthOrTransferEncodingIsSet(string method)
{
using (var input = new TestInput())
{
input.FrameContext.Method = method;
var ex = Assert.Throws(() =>
MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders(), input.FrameContext));
Assert.Equal(StatusCodes.Status411LengthRequired, ex.StatusCode);
Assert.Equal($"{method} request contains no Content-Length or Transfer-Encoding header", ex.Message);
}
}
[Theory]
[InlineData("POST")]
[InlineData("PUT")]
public void ForThrowsWhenMethodRequiresLengthButNoContentLengthSetHttp10(string method)
{
using (var input = new TestInput())
{
input.FrameContext.Method = method;
var ex = Assert.Throws(() =>
MessageBody.For(HttpVersion.Http10, new FrameRequestHeaders(), input.FrameContext));
Assert.Equal(StatusCodes.Status400BadRequest, ex.StatusCode);
Assert.Equal($"{method} request contains no Content-Length header", ex.Message);
}
}
public static IEnumerable