|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Globalization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests.TestTransport;
using Microsoft.AspNetCore.InternalTesting;
using Microsoft.Extensions.Logging.Testing;
using Xunit;
namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests;
public class ChunkedResponseTests : LoggedTest
{
[Fact]
public async Task ResponsesAreChunkedAutomatically()
{
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
var response = httpContext.Response;
await response.BodyWriter.WriteAsync(new Memory<byte>(Encoding.ASCII.GetBytes("Hello "), 0, 6));
await response.BodyWriter.WriteAsync(new Memory<byte>(Encoding.ASCII.GetBytes("World!"), 0, 6));
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
"6",
"Hello ",
"6",
"World!",
"0",
"",
"");
}
}
}
[Fact]
public async Task ResponsesAreNotChunkedAutomaticallyForHttp10Requests()
{
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
await httpContext.Response.WriteAsync("Hello ");
await httpContext.Response.WriteAsync("World!");
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.0",
"Connection: keep-alive",
"",
"");
await connection.ReceiveEnd(
"HTTP/1.1 200 OK",
"Connection: close",
$"Date: {testContext.DateHeaderValue}",
"",
"Hello World!");
}
}
}
[Fact]
public async Task IgnoresChangesToHttpProtocol()
{
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
httpContext.Request.Protocol = "HTTP/2"; // Doesn't support chunking. This change should be ignored.
var response = httpContext.Response;
await response.BodyWriter.WriteAsync(new Memory<byte>(Encoding.ASCII.GetBytes("Hello "), 0, 6));
await response.BodyWriter.WriteAsync(new Memory<byte>(Encoding.ASCII.GetBytes("World!"), 0, 6));
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
"6",
"Hello ",
"6",
"World!",
"0",
"",
"");
}
}
}
[Fact]
public async Task ResponsesAreChunkedAutomaticallyForHttp11NonKeepAliveRequests()
{
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
await httpContext.Response.WriteAsync("Hello ");
await httpContext.Response.WriteAsync("World!");
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"Connection: close",
"",
"");
await connection.ReceiveEnd(
"HTTP/1.1 200 OK",
"Connection: close",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
"6",
"Hello ",
"6",
"World!",
"0",
"",
"");
}
}
}
[Fact]
public async Task ResponsesAreChunkedAutomaticallyLargeResponseWithOverloadedWriteAsync()
{
var testContext = new TestServiceContext(LoggerFactory);
var expectedString = new string('a', 10000);
await using (var server = new TestServer(async httpContext =>
{
await httpContext.Response.WriteAsync(expectedString);
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"Connection: close",
"",
"");
await connection.ReceiveEnd(
"HTTP/1.1 200 OK",
"Connection: close",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
"f92",
new string('a', 3986),
"ff9",
new string('a', 4089),
"785",
new string('a', 1925),
"0",
"",
"");
}
}
}
[Theory]
[InlineData(4096)]
[InlineData(10000)]
[InlineData(100000)]
public async Task ResponsesAreChunkedAutomaticallyLargeChunksLargeResponseWithOverloadedWriteAsync(int length)
{
var testContext = new TestServiceContext(LoggerFactory);
var expectedString = new string('a', length);
await using (var server = new TestServer(async httpContext =>
{
await httpContext.Response.StartAsync();
var memory = httpContext.Response.BodyWriter.GetMemory(length);
Assert.True(length <= memory.Length);
Encoding.ASCII.GetBytes(expectedString).CopyTo(memory);
httpContext.Response.BodyWriter.Advance(length);
await httpContext.Response.BodyWriter.FlushAsync();
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"Connection: close",
"",
"");
await connection.ReceiveEnd(
"HTTP/1.1 200 OK",
"Connection: close",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
length.ToString("x", CultureInfo.InvariantCulture),
new string('a', length),
"0",
"",
"");
}
}
}
[Theory]
[InlineData(1)]
[InlineData(16)]
[InlineData(256)]
[InlineData(4096)]
public async Task ResponsesAreChunkedAutomaticallyPartialWrite(int partialLength)
{
var testContext = new TestServiceContext(LoggerFactory);
var expectedString = new string('a', partialLength);
await using (var server = new TestServer(async httpContext =>
{
await httpContext.Response.StartAsync();
var memory = httpContext.Response.BodyWriter.GetMemory(100000);
Encoding.ASCII.GetBytes(expectedString).CopyTo(memory);
httpContext.Response.BodyWriter.Advance(partialLength);
await httpContext.Response.BodyWriter.FlushAsync();
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"Connection: close",
"",
"");
await connection.ReceiveEnd(
"HTTP/1.1 200 OK",
"Connection: close",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
partialLength.ToString("x", CultureInfo.InvariantCulture),
new string('a', partialLength),
"0",
"",
"");
}
}
}
[Fact]
public async Task SettingConnectionCloseHeaderInAppDoesNotDisableChunking()
{
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
httpContext.Response.Headers["Connection"] = "close";
await httpContext.Response.WriteAsync("Hello ");
await httpContext.Response.WriteAsync("World!");
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.ReceiveEnd(
"HTTP/1.1 200 OK",
"Connection: close",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
"6",
"Hello ",
"6",
"World!",
"0",
"",
"");
}
}
}
[Fact]
public async Task ZeroLengthWritesAreIgnored()
{
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
var response = httpContext.Response;
await response.BodyWriter.WriteAsync(new Memory<byte>(Encoding.ASCII.GetBytes("Hello "), 0, 6));
await response.BodyWriter.WriteAsync(new Memory<byte>(new byte[0], 0, 0));
await response.BodyWriter.WriteAsync(new Memory<byte>(Encoding.ASCII.GetBytes("World!"), 0, 6));
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.Receive(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
"6",
"Hello ",
"6",
"World!",
"0",
"",
"");
}
}
}
[Fact]
public async Task ZeroLengthWritesFlushHeaders()
{
var testContext = new TestServiceContext(LoggerFactory);
var flushed = new SemaphoreSlim(0, 1);
await using (var server = new TestServer(async httpContext =>
{
var response = httpContext.Response;
await response.WriteAsync("");
await flushed.WaitAsync();
await response.WriteAsync("Hello World!");
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.Receive(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
"");
flushed.Release();
await connection.Receive(
"c",
"Hello World!",
"0",
"",
"");
}
}
}
[Fact]
public async Task EmptyResponseBodyHandledCorrectlyWithZeroLengthWrite()
{
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
var response = httpContext.Response;
await response.BodyWriter.WriteAsync(new Memory<byte>(new byte[0], 0, 0));
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.Receive(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
"0",
"",
"");
}
}
}
[Fact]
public async Task ConnectionClosedIfExceptionThrownAfterWrite()
{
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
var response = httpContext.Response;
await response.BodyWriter.WriteAsync(new Memory<byte>(Encoding.ASCII.GetBytes("Hello World!"), 0, 12));
throw new Exception();
}, testContext))
{
using (var connection = server.CreateConnection())
{
// SendEnd is not called, so it isn't the client closing the connection.
// client closing the connection.
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.ReceiveEnd(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
"c",
"Hello World!",
"");
}
}
}
[Fact]
public async Task ConnectionClosedIfExceptionThrownAfterZeroLengthWrite()
{
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
var response = httpContext.Response;
await response.BodyWriter.WriteAsync(new Memory<byte>(new byte[0], 0, 0));
throw new Exception();
}, testContext))
{
using (var connection = server.CreateConnection())
{
// SendEnd is not called, so it isn't the client closing the connection.
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
// Headers are sent before connection is closed, but chunked body terminator isn't sent
await connection.ReceiveEnd(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
"");
}
}
}
[Fact]
public async Task WritesAreFlushedPriorToResponseCompletion()
{
var testContext = new TestServiceContext(LoggerFactory);
var flushWh = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
await using (var server = new TestServer(async httpContext =>
{
var response = httpContext.Response;
await response.BodyWriter.WriteAsync(new Memory<byte>(Encoding.ASCII.GetBytes("Hello "), 0, 6));
// Don't complete response until client has received the first chunk.
await flushWh.Task.DefaultTimeout();
await response.BodyWriter.WriteAsync(new Memory<byte>(Encoding.ASCII.GetBytes("World!"), 0, 6));
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.Receive(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
"6",
"Hello ",
"");
flushWh.SetResult();
await connection.Receive(
"6",
"World!",
"0",
"",
"");
}
}
}
[Fact]
public async Task ChunksCanBeWrittenManually()
{
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
var response = httpContext.Response;
response.Headers["Transfer-Encoding"] = "chunked";
await response.BodyWriter.WriteAsync(new Memory<byte>(Encoding.ASCII.GetBytes("6\r\nHello \r\n"), 0, 11));
await response.BodyWriter.WriteAsync(new Memory<byte>(Encoding.ASCII.GetBytes("6\r\nWorld!\r\n"), 0, 11));
await response.BodyWriter.WriteAsync(new Memory<byte>(Encoding.ASCII.GetBytes("0\r\n\r\n"), 0, 5));
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.Receive(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
"6",
"Hello ",
"6",
"World!",
"0",
"",
"");
}
}
}
[Fact]
public async Task ChunksWithGetMemoryAfterStartAsyncBeforeFirstFlushStillFlushes()
{
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
var response = httpContext.Response;
await response.StartAsync();
var memory = response.BodyWriter.GetMemory();
var fisrtPartOfResponse = Encoding.ASCII.GetBytes("Hello ");
fisrtPartOfResponse.CopyTo(memory);
response.BodyWriter.Advance(6);
memory = response.BodyWriter.GetMemory();
var secondPartOfResponse = Encoding.ASCII.GetBytes("World!");
secondPartOfResponse.CopyTo(memory);
response.BodyWriter.Advance(6);
await response.BodyWriter.FlushAsync();
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.Receive(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
"c",
"Hello World!",
"0",
"",
"");
}
}
}
[Fact]
public async Task ChunksWithGetMemoryBeforeFirstFlushStillFlushes()
{
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
var response = httpContext.Response;
var memory = response.BodyWriter.GetMemory();
var fisrtPartOfResponse = Encoding.ASCII.GetBytes("Hello ");
fisrtPartOfResponse.CopyTo(memory);
response.BodyWriter.Advance(6);
memory = response.BodyWriter.GetMemory();
var secondPartOfResponse = Encoding.ASCII.GetBytes("World!");
secondPartOfResponse.CopyTo(memory);
response.BodyWriter.Advance(6);
await response.BodyWriter.FlushAsync();
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.Receive(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
"c",
"Hello World!",
"0",
"",
"");
}
}
}
[Fact]
public async Task ChunksWithGetMemoryLargeWriteBeforeFirstFlush()
{
var length = new IntAsRef();
var semaphore = new SemaphoreSlim(initialCount: 0);
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
var response = httpContext.Response;
var memory = response.BodyWriter.GetMemory(5000);
length.Value = memory.Length;
semaphore.Release();
var fisrtPartOfResponse = Encoding.ASCII.GetBytes(new string('a', memory.Length));
fisrtPartOfResponse.CopyTo(memory);
response.BodyWriter.Advance(memory.Length);
memory = response.BodyWriter.GetMemory();
var secondPartOfResponse = Encoding.ASCII.GetBytes("World!");
secondPartOfResponse.CopyTo(memory);
response.BodyWriter.Advance(6);
await response.BodyWriter.FlushAsync();
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
// Wait for length to be set
await semaphore.WaitAsync();
await connection.Receive(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
length.Value.ToString("x", CultureInfo.InvariantCulture),
new string('a', length.Value),
"6",
"World!",
"0",
"",
"");
}
}
}
[Fact]
public async Task ChunksWithGetMemoryAndStartAsyncWithInitialFlushWorks()
{
var length = new IntAsRef();
var semaphore = new SemaphoreSlim(initialCount: 0);
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
var response = httpContext.Response;
await response.BodyWriter.FlushAsync();
var memory = response.BodyWriter.GetMemory(5000);
length.Value = memory.Length;
semaphore.Release();
var fisrtPartOfResponse = Encoding.ASCII.GetBytes(new string('a', memory.Length));
fisrtPartOfResponse.CopyTo(memory);
response.BodyWriter.Advance(memory.Length);
memory = response.BodyWriter.GetMemory();
var secondPartOfResponse = Encoding.ASCII.GetBytes("World!");
secondPartOfResponse.CopyTo(memory);
response.BodyWriter.Advance(6);
await response.BodyWriter.FlushAsync();
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
// Wait for length to be set
await semaphore.WaitAsync();
await connection.Receive(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
length.Value.ToString("x", CultureInfo.InvariantCulture),
new string('a', length.Value),
"6",
"World!",
"0",
"",
"");
}
}
}
[Fact]
public async Task ChunksWithGetMemoryBeforeFlushEdgeCase()
{
var length = 0;
var semaphore = new SemaphoreSlim(initialCount: 0);
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
var response = httpContext.Response;
await response.StartAsync();
var memory = response.BodyWriter.GetMemory();
length = memory.Length - 1;
semaphore.Release();
var fisrtPartOfResponse = Encoding.ASCII.GetBytes(new string('a', length));
fisrtPartOfResponse.CopyTo(memory);
response.BodyWriter.Advance(length);
var secondMemory = response.BodyWriter.GetMemory(6);
var secondPartOfResponse = Encoding.ASCII.GetBytes("World!");
secondPartOfResponse.CopyTo(secondMemory);
response.BodyWriter.Advance(6);
await response.BodyWriter.FlushAsync();
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
// Wait for length to be set
await semaphore.WaitAsync();
await connection.Receive(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
length.ToString("x", CultureInfo.InvariantCulture),
new string('a', length),
"6",
"World!",
"0",
"",
"");
}
}
}
[Fact]
public async Task ChunkGetMemoryMultipleAdvance()
{
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
var response = httpContext.Response;
await response.StartAsync();
var memory = response.BodyWriter.GetMemory(4096);
var fisrtPartOfResponse = Encoding.ASCII.GetBytes("Hello ");
fisrtPartOfResponse.CopyTo(memory);
response.BodyWriter.Advance(6);
var secondPartOfResponse = Encoding.ASCII.GetBytes("World!");
secondPartOfResponse.CopyTo(memory.Slice(6));
response.BodyWriter.Advance(6);
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.Receive(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
"c",
"Hello World!",
"0",
"",
"");
}
}
}
[Fact]
public async Task ChunkGetSpanMultipleAdvance()
{
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
var response = httpContext.Response;
await response.StartAsync();
// To avoid using span in an async method
void NonAsyncMethod()
{
var span = response.BodyWriter.GetSpan(4096);
var fisrtPartOfResponse = Encoding.ASCII.GetBytes("Hello ");
fisrtPartOfResponse.CopyTo(span);
response.BodyWriter.Advance(6);
var secondPartOfResponse = Encoding.ASCII.GetBytes("World!");
secondPartOfResponse.CopyTo(span.Slice(6));
response.BodyWriter.Advance(6);
}
NonAsyncMethod();
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.Receive(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
"c",
"Hello World!",
"0",
"",
"");
}
}
}
[Fact]
public async Task ChunkGetMemoryAndWrite()
{
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
var response = httpContext.Response;
await response.StartAsync();
var memory = response.BodyWriter.GetMemory(4096);
var fisrtPartOfResponse = Encoding.ASCII.GetBytes("Hello ");
fisrtPartOfResponse.CopyTo(memory);
response.BodyWriter.Advance(6);
await response.WriteAsync("World!");
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.Receive(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
"c",
"Hello World!",
"0",
"",
"");
}
}
}
[Fact]
public async Task ChunkGetMemoryAndWriteWithoutStart()
{
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
var response = httpContext.Response;
var memory = response.BodyWriter.GetMemory(4096);
var fisrtPartOfResponse = Encoding.ASCII.GetBytes("Hello ");
fisrtPartOfResponse.CopyTo(memory);
response.BodyWriter.Advance(6);
await response.WriteAsync("World!");
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.Receive(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
"6",
"Hello ",
"6",
"World!",
"0",
"",
"");
}
}
}
[Fact]
public async Task GetMemoryWithSizeHint()
{
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
var response = httpContext.Response;
await response.StartAsync();
var memory = response.BodyWriter.GetMemory(0);
// Headers are already written to memory, sliced appropriately
Assert.Equal(4005, memory.Length);
memory = response.BodyWriter.GetMemory(1000000);
Assert.Equal(4005, memory.Length);
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.Receive(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
"0",
"",
"");
}
}
}
[Fact]
public async Task GetMemoryWithSizeHintWithoutStartAsync()
{
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
var response = httpContext.Response;
var memory = response.BodyWriter.GetMemory(0);
Assert.Equal(4096, memory.Length);
memory = response.BodyWriter.GetMemory(1000000);
Assert.True(memory.Length >= 1000000);
await Task.CompletedTask;
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.Receive(
"HTTP/1.1 200 OK",
"Content-Length: 0",
$"Date: {testContext.DateHeaderValue}",
"",
"");
}
}
}
[Theory]
[InlineData(15)]
[InlineData(255)]
public async Task ChunkGetMemoryWithoutStartWithSmallerSizesWork(int writeSize)
{
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
var response = httpContext.Response;
var memory = response.BodyWriter.GetMemory(4096);
var fisrtPartOfResponse = Encoding.ASCII.GetBytes(new string('a', writeSize));
fisrtPartOfResponse.CopyTo(memory);
response.BodyWriter.Advance(writeSize);
await response.BodyWriter.FlushAsync();
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.Receive(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
writeSize.ToString("X", CultureInfo.InvariantCulture).ToLowerInvariant(),
new string('a', writeSize),
"0",
"",
"");
}
}
}
[Theory]
[InlineData(15)]
[InlineData(255)]
public async Task ChunkGetMemoryWithStartWithSmallerSizesWork(int writeSize)
{
var testContext = new TestServiceContext(LoggerFactory);
await using (var server = new TestServer(async httpContext =>
{
var response = httpContext.Response;
var memory = response.BodyWriter.GetMemory(4096);
var fisrtPartOfResponse = Encoding.ASCII.GetBytes(new string('a', writeSize));
fisrtPartOfResponse.CopyTo(memory);
response.BodyWriter.Advance(writeSize);
await response.BodyWriter.FlushAsync();
}, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host: ",
"",
"");
await connection.Receive(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
writeSize.ToString("X", CultureInfo.InvariantCulture).ToLowerInvariant(),
new string('a', writeSize),
"0",
"",
"");
}
}
}
[Fact]
public async Task ChunkedWithBothPipeAndStreamWorks()
{
await using (var server = new TestServer(async httpContext =>
{
var response = httpContext.Response;
var memory = response.BodyWriter.GetMemory(4096);
var fisrtPartOfResponse = Encoding.ASCII.GetBytes("hello,");
fisrtPartOfResponse.CopyTo(memory);
response.BodyWriter.Advance(6);
var secondPartOfResponse = Encoding.ASCII.GetBytes(" world");
secondPartOfResponse.CopyTo(memory.Slice(6));
response.BodyWriter.Advance(6);
await response.Body.WriteAsync(Encoding.ASCII.GetBytes("hello, world"));
await response.BodyWriter.WriteAsync(Encoding.ASCII.GetBytes("hello, world"));
await response.WriteAsync("hello, world");
}, new TestServiceContext(LoggerFactory)))
{
using (var connection = server.CreateConnection())
{
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
"HTTP/1.1 200 OK",
$"Date: {server.Context.DateHeaderValue}",
"Transfer-Encoding: chunked",
"",
"c",
"hello, world",
"c",
"hello, world",
"c",
"hello, world",
"c",
"hello, world",
"0",
"",
"");
}
}
}
private class IntAsRef
{
public int Value { get; set; }
}
}
|