183 lines
7.0 KiB
C#
183 lines
7.0 KiB
C#
// 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.Buffers;
|
|
using System.IO.Pipelines;
|
|
using System.Text;
|
|
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
|
|
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
|
|
using Xunit;
|
|
|
|
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|
{
|
|
public class PipelineExtensionTests : IDisposable
|
|
{
|
|
// ulong.MaxValue.ToString().Length
|
|
private const int _ulongMaxValueLength = 20;
|
|
|
|
private readonly Pipe _pipe;
|
|
private readonly MemoryPool<byte> _memoryPool = KestrelMemoryPool.Create();
|
|
|
|
public PipelineExtensionTests()
|
|
{
|
|
_pipe = new Pipe(new PipeOptions(_memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false));
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_pipe.Reader.Complete();
|
|
_pipe.Writer.Complete();
|
|
_memoryPool.Dispose();
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(ulong.MinValue)]
|
|
[InlineData(ulong.MaxValue)]
|
|
[InlineData(4_8_15_16_23_42)]
|
|
public void WritesNumericToAscii(ulong number)
|
|
{
|
|
var writerBuffer = _pipe.Writer;
|
|
var writer = new BufferWriter<PipeWriter>(writerBuffer);
|
|
writer.WriteNumeric(number);
|
|
writer.Commit();
|
|
writerBuffer.FlushAsync().GetAwaiter().GetResult();
|
|
|
|
var reader = _pipe.Reader.ReadAsync().GetAwaiter().GetResult();
|
|
var numAsStr = number.ToString();
|
|
var expected = Encoding.ASCII.GetBytes(numAsStr);
|
|
AssertExtensions.Equal(expected, reader.Buffer.Slice(0, numAsStr.Length).ToArray());
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(1)]
|
|
[InlineData(_ulongMaxValueLength / 2)]
|
|
[InlineData(_ulongMaxValueLength - 1)]
|
|
public void WritesNumericAcrossSpanBoundaries(int gapSize)
|
|
{
|
|
var writerBuffer = _pipe.Writer;
|
|
var writer = new BufferWriter<PipeWriter>(writerBuffer);
|
|
// almost fill up the first block
|
|
var spacer = new byte[writer.Span.Length - gapSize];
|
|
writer.Write(spacer);
|
|
|
|
var bufferLength = writer.Span.Length;
|
|
writer.WriteNumeric(ulong.MaxValue);
|
|
Assert.NotEqual(bufferLength, writer.Span.Length);
|
|
writer.Commit();
|
|
writerBuffer.FlushAsync().GetAwaiter().GetResult();
|
|
|
|
var reader = _pipe.Reader.ReadAsync().GetAwaiter().GetResult();
|
|
var numAsString = ulong.MaxValue.ToString();
|
|
var written = reader.Buffer.Slice(spacer.Length, numAsString.Length);
|
|
Assert.False(written.IsSingleSegment, "The buffer should cross spans");
|
|
AssertExtensions.Equal(Encoding.ASCII.GetBytes(numAsString), written.ToArray());
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("\0abcxyz", new byte[] { 0, 97, 98, 99, 120, 121, 122 })]
|
|
[InlineData("!#$%i", new byte[] { 33, 35, 36, 37, 105 })]
|
|
[InlineData("!#$%", new byte[] { 33, 35, 36, 37 })]
|
|
[InlineData("!#$", new byte[] { 33, 35, 36 })]
|
|
[InlineData("!#", new byte[] { 33, 35 })]
|
|
[InlineData("!", new byte[] { 33 })]
|
|
// null or empty
|
|
[InlineData("", new byte[0])]
|
|
[InlineData(null, new byte[0])]
|
|
public void EncodesAsAscii(string input, byte[] expected)
|
|
{
|
|
var pipeWriter = _pipe.Writer;
|
|
var writer = new BufferWriter<PipeWriter>(pipeWriter);
|
|
writer.WriteAsciiNoValidation(input);
|
|
writer.Commit();
|
|
pipeWriter.FlushAsync().GetAwaiter().GetResult();
|
|
pipeWriter.Complete();
|
|
|
|
var reader = _pipe.Reader.ReadAsync().GetAwaiter().GetResult();
|
|
|
|
if (expected.Length > 0)
|
|
{
|
|
AssertExtensions.Equal(
|
|
expected,
|
|
reader.Buffer.ToArray());
|
|
}
|
|
else
|
|
{
|
|
Assert.Equal(0, reader.Buffer.Length);
|
|
}
|
|
}
|
|
|
|
[Theory]
|
|
// non-ascii characters stored in 32 bits
|
|
[InlineData("𤭢𐐝")]
|
|
// non-ascii characters stored in 16 bits
|
|
[InlineData("ñ٢⛄⛵")]
|
|
public void WriteAsciiNoValidationWritesOnlyOneBytePerChar(string input)
|
|
{
|
|
// WriteAscii doesn't validate if characters are in the ASCII range
|
|
// but it shouldn't produce more than one byte per character
|
|
var writerBuffer = _pipe.Writer;
|
|
var writer = new BufferWriter<PipeWriter>(writerBuffer);
|
|
writer.WriteAsciiNoValidation(input);
|
|
writer.Commit();
|
|
writerBuffer.FlushAsync().GetAwaiter().GetResult();
|
|
var reader = _pipe.Reader.ReadAsync().GetAwaiter().GetResult();
|
|
|
|
Assert.Equal(input.Length, reader.Buffer.Length);
|
|
}
|
|
|
|
[Fact]
|
|
public void WriteAsciiNoValidation()
|
|
{
|
|
const byte maxAscii = 0x7f;
|
|
var writerBuffer = _pipe.Writer;
|
|
var writer = new BufferWriter<PipeWriter>(writerBuffer);
|
|
for (var i = 0; i < maxAscii; i++)
|
|
{
|
|
writer.WriteAsciiNoValidation(new string((char)i, 1));
|
|
}
|
|
writer.Commit();
|
|
writerBuffer.FlushAsync().GetAwaiter().GetResult();
|
|
|
|
var reader = _pipe.Reader.ReadAsync().GetAwaiter().GetResult();
|
|
var data = reader.Buffer.Slice(0, maxAscii).ToArray();
|
|
for (var i = 0; i < maxAscii; i++)
|
|
{
|
|
Assert.Equal(i, data[i]);
|
|
}
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(2, 1)]
|
|
[InlineData(3, 1)]
|
|
[InlineData(4, 2)]
|
|
[InlineData(5, 3)]
|
|
[InlineData(7, 4)]
|
|
[InlineData(8, 3)]
|
|
[InlineData(8, 4)]
|
|
[InlineData(8, 5)]
|
|
[InlineData(100, 48)]
|
|
public void WritesAsciiAcrossBlockBoundaries(int stringLength, int gapSize)
|
|
{
|
|
var testString = new string(' ', stringLength);
|
|
var writerBuffer = _pipe.Writer;
|
|
var writer = new BufferWriter<PipeWriter>(writerBuffer);
|
|
// almost fill up the first block
|
|
var spacer = new byte[writer.Span.Length - gapSize];
|
|
writer.Write(spacer);
|
|
Assert.Equal(gapSize, writer.Span.Length);
|
|
|
|
var bufferLength = writer.Span.Length;
|
|
writer.WriteAsciiNoValidation(testString);
|
|
Assert.NotEqual(bufferLength, writer.Span.Length);
|
|
writer.Commit();
|
|
writerBuffer.FlushAsync().GetAwaiter().GetResult();
|
|
|
|
var reader = _pipe.Reader.ReadAsync().GetAwaiter().GetResult();
|
|
var written = reader.Buffer.Slice(spacer.Length, stringLength);
|
|
Assert.False(written.IsSingleSegment, "The buffer should cross spans");
|
|
AssertExtensions.Equal(Encoding.ASCII.GetBytes(testString), written.ToArray());
|
|
}
|
|
}
|
|
}
|