Changing length prefixing to separator for JSON C#
This commit is contained in:
parent
b12451025f
commit
5ad5f36f88
|
|
@ -4,7 +4,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.AspNetCore.Sockets.Internal.Formatters;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.SignalR.Internal.Encoders
|
namespace Microsoft.AspNetCore.SignalR.Internal.Encoders
|
||||||
{
|
{
|
||||||
|
|
@ -13,7 +12,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Encoders
|
||||||
public byte[] Decode(byte[] payload)
|
public byte[] Decode(byte[] payload)
|
||||||
{
|
{
|
||||||
var buffer = new ReadOnlyBuffer<byte>(payload);
|
var buffer = new ReadOnlyBuffer<byte>(payload);
|
||||||
TextMessageParser.TryParseMessage(ref buffer, out var message);
|
LengthPrefixedTextMessageParser.TryParseMessage(ref buffer, out var message);
|
||||||
|
|
||||||
return Convert.FromBase64String(Encoding.UTF8.GetString(message.ToArray()));
|
return Convert.FromBase64String(Encoding.UTF8.GetString(message.ToArray()));
|
||||||
}
|
}
|
||||||
|
|
@ -23,7 +22,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Encoders
|
||||||
var buffer = Encoding.UTF8.GetBytes(Convert.ToBase64String(payload));
|
var buffer = Encoding.UTF8.GetBytes(Convert.ToBase64String(payload));
|
||||||
using (var stream = new MemoryStream())
|
using (var stream = new MemoryStream())
|
||||||
{
|
{
|
||||||
TextMessageFormatter.WriteMessage(buffer, stream);
|
LengthPrefixedTextMessageWriter.WriteMessage(buffer, stream);
|
||||||
return stream.ToArray();
|
return stream.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,185 @@
|
||||||
|
// 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.Text;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.SignalR.Internal.Encoders
|
||||||
|
{
|
||||||
|
public static class LengthPrefixedTextMessageParser
|
||||||
|
{
|
||||||
|
private const int Int32OverflowLength = 10;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to parse a message from the buffer. Returns 'false' if there is not enough data to complete a message. Throws an
|
||||||
|
/// exception if there is a format error in the provided data.
|
||||||
|
/// </summary>
|
||||||
|
public static bool TryParseMessage(ref ReadOnlyBuffer<byte> buffer, out ReadOnlyBuffer<byte> payload)
|
||||||
|
{
|
||||||
|
payload = default(ReadOnlyBuffer<byte>);
|
||||||
|
var span = buffer.Span;
|
||||||
|
|
||||||
|
if (!TryReadLength(span, out var index, out var length))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var remaining = buffer.Slice(index);
|
||||||
|
span = remaining.Span;
|
||||||
|
|
||||||
|
if (!TryReadDelimiter(span, LengthPrefixedTextMessageWriter.FieldDelimiter, "length"))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip the delimeter
|
||||||
|
remaining = remaining.Slice(1);
|
||||||
|
|
||||||
|
if (remaining.Length < length + 1)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
payload = remaining.Slice(0, length);
|
||||||
|
|
||||||
|
remaining = remaining.Slice(length);
|
||||||
|
|
||||||
|
if (!TryReadDelimiter(remaining.Span, LengthPrefixedTextMessageWriter.MessageDelimiter, "payload"))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip the delimeter
|
||||||
|
buffer = remaining.Slice(1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryReadLength(ReadOnlySpan<byte> buffer, out int index, out int length)
|
||||||
|
{
|
||||||
|
length = 0;
|
||||||
|
// Read until the first ':' to find the length
|
||||||
|
index = buffer.IndexOf((byte)LengthPrefixedTextMessageWriter.FieldDelimiter);
|
||||||
|
|
||||||
|
if (index == -1)
|
||||||
|
{
|
||||||
|
// Insufficient data
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lengthSpan = buffer.Slice(0, index);
|
||||||
|
|
||||||
|
if (!TryParseInt32(lengthSpan, out length, out var bytesConsumed) || bytesConsumed < lengthSpan.Length)
|
||||||
|
{
|
||||||
|
throw new FormatException($"Invalid length: '{Encoding.UTF8.GetString(lengthSpan.ToArray())}'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryReadDelimiter(ReadOnlySpan<byte> buffer, char delimiter, string field)
|
||||||
|
{
|
||||||
|
if (buffer.Length == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer[0] != delimiter)
|
||||||
|
{
|
||||||
|
throw new FormatException($"Missing delimiter '{delimiter}' after {field}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryParseInt32(ReadOnlySpan<byte> text, out int value, out int bytesConsumed)
|
||||||
|
{
|
||||||
|
if (text.Length < 1)
|
||||||
|
{
|
||||||
|
bytesConsumed = 0;
|
||||||
|
value = default(int);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int indexOfFirstDigit = 0;
|
||||||
|
int sign = 1;
|
||||||
|
if (text[0] == '-')
|
||||||
|
{
|
||||||
|
indexOfFirstDigit = 1;
|
||||||
|
sign = -1;
|
||||||
|
}
|
||||||
|
else if (text[0] == '+')
|
||||||
|
{
|
||||||
|
indexOfFirstDigit = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int overflowLength = Int32OverflowLength + indexOfFirstDigit;
|
||||||
|
|
||||||
|
// Parse the first digit separately. If invalid here, we need to return false.
|
||||||
|
int firstDigit = text[indexOfFirstDigit] - 48; // '0'
|
||||||
|
if (firstDigit < 0 || firstDigit > 9)
|
||||||
|
{
|
||||||
|
bytesConsumed = 0;
|
||||||
|
value = default(int);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int parsedValue = firstDigit;
|
||||||
|
|
||||||
|
if (text.Length < overflowLength)
|
||||||
|
{
|
||||||
|
// Length is less than Int32OverflowLength; overflow is not possible
|
||||||
|
for (int index = indexOfFirstDigit + 1; index < text.Length; index++)
|
||||||
|
{
|
||||||
|
int nextDigit = text[index] - 48; // '0'
|
||||||
|
if (nextDigit < 0 || nextDigit > 9)
|
||||||
|
{
|
||||||
|
bytesConsumed = index;
|
||||||
|
value = parsedValue * sign;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
parsedValue = parsedValue * 10 + nextDigit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Length is greater than Int32OverflowLength; overflow is only possible after Int32OverflowLength
|
||||||
|
// digits. There may be no overflow after Int32OverflowLength if there are leading zeroes.
|
||||||
|
for (int index = indexOfFirstDigit + 1; index < overflowLength - 1; index++)
|
||||||
|
{
|
||||||
|
int nextDigit = text[index] - 48; // '0'
|
||||||
|
if (nextDigit < 0 || nextDigit > 9)
|
||||||
|
{
|
||||||
|
bytesConsumed = index;
|
||||||
|
value = parsedValue * sign;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
parsedValue = parsedValue * 10 + nextDigit;
|
||||||
|
}
|
||||||
|
for (int index = overflowLength - 1; index < text.Length; index++)
|
||||||
|
{
|
||||||
|
int nextDigit = text[index] - 48; // '0'
|
||||||
|
if (nextDigit < 0 || nextDigit > 9)
|
||||||
|
{
|
||||||
|
bytesConsumed = index;
|
||||||
|
value = parsedValue * sign;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// If parsedValue > (int.MaxValue / 10), any more appended digits will cause overflow.
|
||||||
|
// if parsedValue == (int.MaxValue / 10), any nextDigit greater than 7 or 8 (depending on sign) implies overflow.
|
||||||
|
bool positive = sign > 0;
|
||||||
|
bool nextDigitTooLarge = nextDigit > 8 || (positive && nextDigit > 7);
|
||||||
|
if (parsedValue > int.MaxValue / 10 || parsedValue == int.MaxValue / 10 && nextDigitTooLarge)
|
||||||
|
{
|
||||||
|
bytesConsumed = 0;
|
||||||
|
value = default(int);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
parsedValue = parsedValue * 10 + nextDigit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesConsumed = text.Length;
|
||||||
|
value = parsedValue * sign;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
// 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.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.SignalR.Internal.Encoders
|
||||||
|
{
|
||||||
|
public static class LengthPrefixedTextMessageWriter
|
||||||
|
{
|
||||||
|
private const int Int32OverflowLength = 10;
|
||||||
|
|
||||||
|
internal const char FieldDelimiter = ':';
|
||||||
|
internal const char MessageDelimiter = ';';
|
||||||
|
|
||||||
|
public static void WriteMessage(ReadOnlySpan<byte> payload, Stream output)
|
||||||
|
{
|
||||||
|
// Calculate the length, it's the number of characters for text messages, but number of base64 characters for binary
|
||||||
|
|
||||||
|
// Write the length as a string
|
||||||
|
|
||||||
|
// Super inefficient...
|
||||||
|
var lengthString = payload.Length.ToString(CultureInfo.InvariantCulture);
|
||||||
|
var buffer = ArrayPool<byte>.Shared.Rent(Int32OverflowLength);
|
||||||
|
var encodedLength = Encoding.UTF8.GetBytes(lengthString, 0, lengthString.Length, buffer, 0);
|
||||||
|
output.Write(buffer, 0, encodedLength);
|
||||||
|
ArrayPool<byte>.Shared.Return(buffer);
|
||||||
|
|
||||||
|
// Write the field delimiter ':'
|
||||||
|
output.WriteByte((byte)FieldDelimiter);
|
||||||
|
|
||||||
|
buffer = ArrayPool<byte>.Shared.Rent(payload.Length);
|
||||||
|
payload.CopyTo(buffer);
|
||||||
|
output.Write(buffer, 0, payload.Length);
|
||||||
|
ArrayPool<byte>.Shared.Return(buffer);
|
||||||
|
|
||||||
|
// Terminator
|
||||||
|
output.WriteByte((byte)MessageDelimiter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,7 +6,7 @@ using System.Binary;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Sockets.Internal.Formatters
|
namespace Microsoft.AspNetCore.SignalR.Internal.Formatters
|
||||||
{
|
{
|
||||||
public static class BinaryMessageFormatter
|
public static class BinaryMessageFormatter
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Binary;
|
using System.Binary;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Sockets.Internal.Formatters
|
namespace Microsoft.AspNetCore.SignalR.Internal.Formatters
|
||||||
{
|
{
|
||||||
public static class BinaryMessageParser
|
public static class BinaryMessageParser
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,24 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Sockets.Internal.Formatters
|
namespace Microsoft.AspNetCore.SignalR.Internal.Formatters
|
||||||
{
|
{
|
||||||
public static class TextMessageFormatter
|
public static class TextMessageFormatter
|
||||||
{
|
{
|
||||||
private const int Int32OverflowLength = 10;
|
// This record separator is supposed to be used only for JSON payloads where 0x1e character
|
||||||
|
// will not occur (is not a valid character) and therefore it is safe to not escape it
|
||||||
internal const char FieldDelimiter = ':';
|
internal static readonly byte RecordSeparator = 0x1e;
|
||||||
internal const char MessageDelimiter = ';';
|
|
||||||
|
|
||||||
public static void WriteMessage(ReadOnlySpan<byte> payload, Stream output)
|
public static void WriteMessage(ReadOnlySpan<byte> payload, Stream output)
|
||||||
{
|
{
|
||||||
// Calculate the length, it's the number of characters for text messages, but number of base64 characters for binary
|
var buffer = ArrayPool<byte>.Shared.Rent(payload.Length);
|
||||||
|
|
||||||
// Write the length as a string
|
|
||||||
|
|
||||||
// Super inefficient...
|
|
||||||
var lengthString = payload.Length.ToString(CultureInfo.InvariantCulture);
|
|
||||||
var buffer = ArrayPool<byte>.Shared.Rent(Int32OverflowLength);
|
|
||||||
var encodedLength = Encoding.UTF8.GetBytes(lengthString, 0, lengthString.Length, buffer, 0);
|
|
||||||
output.Write(buffer, 0, encodedLength);
|
|
||||||
ArrayPool<byte>.Shared.Return(buffer);
|
|
||||||
|
|
||||||
// Write the field delimiter ':'
|
|
||||||
output.WriteByte((byte)FieldDelimiter);
|
|
||||||
|
|
||||||
buffer = ArrayPool<byte>.Shared.Rent(payload.Length);
|
|
||||||
payload.CopyTo(buffer);
|
payload.CopyTo(buffer);
|
||||||
output.Write(buffer, 0, payload.Length);
|
output.Write(buffer, 0, payload.Length);
|
||||||
ArrayPool<byte>.Shared.Return(buffer);
|
output.WriteByte(RecordSeparator);
|
||||||
|
|
||||||
// Terminator
|
|
||||||
output.WriteByte((byte)MessageDelimiter);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,184 +2,27 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Sockets.Internal.Formatters
|
namespace Microsoft.AspNetCore.SignalR.Internal.Formatters
|
||||||
{
|
{
|
||||||
public static class TextMessageParser
|
public static class TextMessageParser
|
||||||
{
|
{
|
||||||
private const int Int32OverflowLength = 10;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Attempts to parse a message from the buffer. Returns 'false' if there is not enough data to complete a message. Throws an
|
|
||||||
/// exception if there is a format error in the provided data.
|
|
||||||
/// </summary>
|
|
||||||
public static bool TryParseMessage(ref ReadOnlyBuffer<byte> buffer, out ReadOnlyBuffer<byte> payload)
|
public static bool TryParseMessage(ref ReadOnlyBuffer<byte> buffer, out ReadOnlyBuffer<byte> payload)
|
||||||
{
|
{
|
||||||
payload = default(ReadOnlyBuffer<byte>);
|
payload = default(ReadOnlyBuffer<byte>);
|
||||||
var span = buffer.Span;
|
|
||||||
|
|
||||||
if (!TryReadLength(span, out var index, out var length))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var remaining = buffer.Slice(index);
|
|
||||||
span = remaining.Span;
|
|
||||||
|
|
||||||
if (!TryReadDelimiter(span, TextMessageFormatter.FieldDelimiter, "length"))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip the delimeter
|
|
||||||
remaining = remaining.Slice(1);
|
|
||||||
|
|
||||||
if (remaining.Length < length + 1)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
payload = remaining.Slice(0, length);
|
|
||||||
|
|
||||||
remaining = remaining.Slice(length);
|
|
||||||
|
|
||||||
if (!TryReadDelimiter(remaining.Span, TextMessageFormatter.MessageDelimiter, "payload"))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip the delimeter
|
|
||||||
buffer = remaining.Slice(1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryReadLength(ReadOnlySpan<byte> buffer, out int index, out int length)
|
|
||||||
{
|
|
||||||
length = 0;
|
|
||||||
// Read until the first ':' to find the length
|
|
||||||
index = buffer.IndexOf((byte)TextMessageFormatter.FieldDelimiter);
|
|
||||||
|
|
||||||
|
var index = buffer.Span.IndexOf(TextMessageFormatter.RecordSeparator);
|
||||||
if (index == -1)
|
if (index == -1)
|
||||||
{
|
{
|
||||||
// Insufficient data
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var lengthSpan = buffer.Slice(0, index);
|
payload = buffer.Slice(0, index);
|
||||||
|
|
||||||
if (!TryParseInt32(lengthSpan, out length, out var bytesConsumed) || bytesConsumed < lengthSpan.Length)
|
// Skip record separator
|
||||||
{
|
buffer = buffer.Slice(index + 1);
|
||||||
throw new FormatException($"Invalid length: '{Encoding.UTF8.GetString(lengthSpan.ToArray())}'");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryReadDelimiter(ReadOnlySpan<byte> buffer, char delimiter, string field)
|
|
||||||
{
|
|
||||||
if (buffer.Length == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer[0] != delimiter)
|
|
||||||
{
|
|
||||||
throw new FormatException($"Missing delimiter '{delimiter}' after {field}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryParseInt32(ReadOnlySpan<byte> text, out int value, out int bytesConsumed)
|
|
||||||
{
|
|
||||||
if (text.Length < 1)
|
|
||||||
{
|
|
||||||
bytesConsumed = 0;
|
|
||||||
value = default(int);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int indexOfFirstDigit = 0;
|
|
||||||
int sign = 1;
|
|
||||||
if (text[0] == '-')
|
|
||||||
{
|
|
||||||
indexOfFirstDigit = 1;
|
|
||||||
sign = -1;
|
|
||||||
}
|
|
||||||
else if (text[0] == '+')
|
|
||||||
{
|
|
||||||
indexOfFirstDigit = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int overflowLength = Int32OverflowLength + indexOfFirstDigit;
|
|
||||||
|
|
||||||
// Parse the first digit separately. If invalid here, we need to return false.
|
|
||||||
int firstDigit = text[indexOfFirstDigit] - 48; // '0'
|
|
||||||
if (firstDigit < 0 || firstDigit > 9)
|
|
||||||
{
|
|
||||||
bytesConsumed = 0;
|
|
||||||
value = default(int);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int parsedValue = firstDigit;
|
|
||||||
|
|
||||||
if (text.Length < overflowLength)
|
|
||||||
{
|
|
||||||
// Length is less than Int32OverflowLength; overflow is not possible
|
|
||||||
for (int index = indexOfFirstDigit + 1; index < text.Length; index++)
|
|
||||||
{
|
|
||||||
int nextDigit = text[index] - 48; // '0'
|
|
||||||
if (nextDigit < 0 || nextDigit > 9)
|
|
||||||
{
|
|
||||||
bytesConsumed = index;
|
|
||||||
value = parsedValue * sign;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
parsedValue = parsedValue * 10 + nextDigit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Length is greater than Int32OverflowLength; overflow is only possible after Int32OverflowLength
|
|
||||||
// digits. There may be no overflow after Int32OverflowLength if there are leading zeroes.
|
|
||||||
for (int index = indexOfFirstDigit + 1; index < overflowLength - 1; index++)
|
|
||||||
{
|
|
||||||
int nextDigit = text[index] - 48; // '0'
|
|
||||||
if (nextDigit < 0 || nextDigit > 9)
|
|
||||||
{
|
|
||||||
bytesConsumed = index;
|
|
||||||
value = parsedValue * sign;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
parsedValue = parsedValue * 10 + nextDigit;
|
|
||||||
}
|
|
||||||
for (int index = overflowLength - 1; index < text.Length; index++)
|
|
||||||
{
|
|
||||||
int nextDigit = text[index] - 48; // '0'
|
|
||||||
if (nextDigit < 0 || nextDigit > 9)
|
|
||||||
{
|
|
||||||
bytesConsumed = index;
|
|
||||||
value = parsedValue * sign;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// If parsedValue > (int.MaxValue / 10), any more appended digits will cause overflow.
|
|
||||||
// if parsedValue == (int.MaxValue / 10), any nextDigit greater than 7 or 8 (depending on sign) implies overflow.
|
|
||||||
bool positive = sign > 0;
|
|
||||||
bool nextDigitTooLarge = nextDigit > 8 || (positive && nextDigit > 7);
|
|
||||||
if (parsedValue > int.MaxValue / 10 || parsedValue == int.MaxValue / 10 && nextDigitTooLarge)
|
|
||||||
{
|
|
||||||
bytesConsumed = 0;
|
|
||||||
value = default(int);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
parsedValue = parsedValue * 10 + nextDigit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bytesConsumed = text.Length;
|
|
||||||
value = parsedValue * sign;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.AspNetCore.Sockets.Internal.Formatters;
|
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.AspNetCore.Sockets.Internal.Formatters;
|
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
|
||||||
using MsgPack;
|
using MsgPack;
|
||||||
using MsgPack.Serialization;
|
using MsgPack.Serialization;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.AspNetCore.Sockets.Internal.Formatters;
|
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
await connection.ReadSentTextMessageAsync().OrTimeout();
|
await connection.ReadSentTextMessageAsync().OrTimeout();
|
||||||
var invokeMessage = await connection.ReadSentTextMessageAsync().OrTimeout();
|
var invokeMessage = await connection.ReadSentTextMessageAsync().OrTimeout();
|
||||||
|
|
||||||
Assert.Equal("78:{\"invocationId\":\"1\",\"type\":1,\"target\":\"Foo\",\"nonBlocking\":true,\"arguments\":[]};", invokeMessage);
|
Assert.Equal("{\"invocationId\":\"1\",\"type\":1,\"target\":\"Foo\",\"nonBlocking\":true,\"arguments\":[]}\u001e", invokeMessage);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
@ -56,7 +56,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
await hubConnection.StartAsync();
|
await hubConnection.StartAsync();
|
||||||
var negotiationMessage = await connection.ReadSentTextMessageAsync().OrTimeout();
|
var negotiationMessage = await connection.ReadSentTextMessageAsync().OrTimeout();
|
||||||
|
|
||||||
Assert.Equal("19:{\"protocol\":\"json\"};", negotiationMessage);
|
Assert.Equal("{\"protocol\":\"json\"}\u001e", negotiationMessage);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
@ -80,7 +80,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
await connection.ReadSentTextMessageAsync().OrTimeout();
|
await connection.ReadSentTextMessageAsync().OrTimeout();
|
||||||
var invokeMessage = await connection.ReadSentTextMessageAsync().OrTimeout();
|
var invokeMessage = await connection.ReadSentTextMessageAsync().OrTimeout();
|
||||||
|
|
||||||
Assert.Equal("59:{\"invocationId\":\"1\",\"type\":1,\"target\":\"Foo\",\"arguments\":[]};", invokeMessage);
|
Assert.Equal("{\"invocationId\":\"1\",\"type\":1,\"target\":\"Foo\",\"arguments\":[]}\u001e", invokeMessage);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
@ -104,7 +104,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
await connection.ReadSentTextMessageAsync().OrTimeout();
|
await connection.ReadSentTextMessageAsync().OrTimeout();
|
||||||
var invokeMessage = await connection.ReadSentTextMessageAsync().OrTimeout();
|
var invokeMessage = await connection.ReadSentTextMessageAsync().OrTimeout();
|
||||||
|
|
||||||
Assert.Equal("59:{\"invocationId\":\"1\",\"type\":1,\"target\":\"Foo\",\"arguments\":[]};", invokeMessage);
|
Assert.Equal("{\"invocationId\":\"1\",\"type\":1,\"target\":\"Foo\",\"arguments\":[]}\u001e", invokeMessage);
|
||||||
|
|
||||||
// Complete the channel
|
// Complete the channel
|
||||||
await connection.ReceiveJsonMessage(new { invocationId = "1", type = 3 }).OrTimeout();
|
await connection.ReceiveJsonMessage(new { invocationId = "1", type = 3 }).OrTimeout();
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Sockets.Internal.Formatters;
|
using Microsoft.AspNetCore.Sockets.Internal.Formatters;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Sockets.Common.Tests.Internal.Formatters
|
namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
{
|
{
|
||||||
public class ServerSentEventsParserTests
|
public class ServerSentEventsParserTests
|
||||||
{
|
{
|
||||||
|
|
@ -8,10 +8,10 @@ using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Tasks.Channels;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
|
||||||
using Microsoft.AspNetCore.Sockets;
|
using Microsoft.AspNetCore.Sockets;
|
||||||
using Microsoft.AspNetCore.Sockets.Client;
|
using Microsoft.AspNetCore.Sockets.Client;
|
||||||
using Microsoft.AspNetCore.Sockets.Features;
|
using Microsoft.AspNetCore.Sockets.Features;
|
||||||
using Microsoft.AspNetCore.Sockets.Internal.Formatters;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.AspNetCore.Sockets.Internal.Formatters;
|
using Microsoft.AspNetCore.SignalR.Internal.Encoders;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Sockets.Tests.Internal.Formatters
|
namespace Microsoft.AspNetCore.SignalR.Tests.Internal.Encoders
|
||||||
{
|
{
|
||||||
public class TextMessageFormatterTests
|
public class LengthPrefixedTextMessageFormatterTests
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void WriteMultipleMessages()
|
public void WriteMultipleMessages()
|
||||||
|
|
@ -24,12 +23,12 @@ namespace Microsoft.AspNetCore.Sockets.Tests.Internal.Formatters
|
||||||
var output = new MemoryStream();
|
var output = new MemoryStream();
|
||||||
foreach (var message in messages)
|
foreach (var message in messages)
|
||||||
{
|
{
|
||||||
TextMessageFormatter.WriteMessage(message, output);
|
LengthPrefixedTextMessageWriter.WriteMessage(message, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.Equal(expectedEncoding, Encoding.UTF8.GetString(output.ToArray()));
|
Assert.Equal(expectedEncoding, Encoding.UTF8.GetString(output.ToArray()));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(8, "0:;", "")]
|
[InlineData(8, "0:;", "")]
|
||||||
[InlineData(8, "3:ABC;", "ABC")]
|
[InlineData(8, "3:ABC;", "ABC")]
|
||||||
|
|
@ -40,7 +39,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests.Internal.Formatters
|
||||||
var message = Encoding.UTF8.GetBytes(payload);
|
var message = Encoding.UTF8.GetBytes(payload);
|
||||||
var output = new MemoryStream();
|
var output = new MemoryStream();
|
||||||
|
|
||||||
TextMessageFormatter.WriteMessage(message, output);
|
LengthPrefixedTextMessageWriter.WriteMessage(message, output);
|
||||||
|
|
||||||
Assert.Equal(encoded, Encoding.UTF8.GetString(output.ToArray()));
|
Assert.Equal(encoded, Encoding.UTF8.GetString(output.ToArray()));
|
||||||
}
|
}
|
||||||
|
|
@ -2,15 +2,14 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.AspNetCore.Sockets.Internal.Formatters;
|
using Microsoft.AspNetCore.SignalR.Internal.Encoders;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Sockets.Common.Tests.Internal.Formatters
|
namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Encoders
|
||||||
{
|
{
|
||||||
public class TextMessageParserTests
|
public class LengthPrefixedTextMessageParserTests
|
||||||
{
|
{
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(0, "0:;", "")]
|
[InlineData(0, "0:;", "")]
|
||||||
|
|
@ -21,7 +20,7 @@ namespace Microsoft.AspNetCore.Sockets.Common.Tests.Internal.Formatters
|
||||||
{
|
{
|
||||||
ReadOnlyBuffer<byte> buffer = Encoding.UTF8.GetBytes(encoded);
|
ReadOnlyBuffer<byte> buffer = Encoding.UTF8.GetBytes(encoded);
|
||||||
|
|
||||||
Assert.True(TextMessageParser.TryParseMessage(ref buffer, out var message));
|
Assert.True(LengthPrefixedTextMessageParser.TryParseMessage(ref buffer, out var message));
|
||||||
Assert.Equal(0, buffer.Length);
|
Assert.Equal(0, buffer.Length);
|
||||||
Assert.Equal(Encoding.UTF8.GetBytes(payload), message.ToArray());
|
Assert.Equal(Encoding.UTF8.GetBytes(payload), message.ToArray());
|
||||||
}
|
}
|
||||||
|
|
@ -33,7 +32,7 @@ namespace Microsoft.AspNetCore.Sockets.Common.Tests.Internal.Formatters
|
||||||
ReadOnlyBuffer<byte> buffer = Encoding.UTF8.GetBytes(encoded);
|
ReadOnlyBuffer<byte> buffer = Encoding.UTF8.GetBytes(encoded);
|
||||||
|
|
||||||
var messages = new List<byte[]>();
|
var messages = new List<byte[]>();
|
||||||
while (TextMessageParser.TryParseMessage(ref buffer, out var message))
|
while (LengthPrefixedTextMessageParser.TryParseMessage(ref buffer, out var message))
|
||||||
{
|
{
|
||||||
messages.Add(message.ToArray());
|
messages.Add(message.ToArray());
|
||||||
}
|
}
|
||||||
|
|
@ -56,7 +55,7 @@ namespace Microsoft.AspNetCore.Sockets.Common.Tests.Internal.Formatters
|
||||||
public void ReadIncompleteMessages(string encoded)
|
public void ReadIncompleteMessages(string encoded)
|
||||||
{
|
{
|
||||||
ReadOnlyBuffer<byte> buffer = Encoding.UTF8.GetBytes(encoded);
|
ReadOnlyBuffer<byte> buffer = Encoding.UTF8.GetBytes(encoded);
|
||||||
Assert.False(TextMessageParser.TryParseMessage(ref buffer, out _));
|
Assert.False(LengthPrefixedTextMessageParser.TryParseMessage(ref buffer, out _));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -70,7 +69,7 @@ namespace Microsoft.AspNetCore.Sockets.Common.Tests.Internal.Formatters
|
||||||
ReadOnlyBuffer<byte> buffer = Encoding.UTF8.GetBytes(encoded);
|
ReadOnlyBuffer<byte> buffer = Encoding.UTF8.GetBytes(encoded);
|
||||||
var ex = Assert.Throws<FormatException>(() =>
|
var ex = Assert.Throws<FormatException>(() =>
|
||||||
{
|
{
|
||||||
TextMessageParser.TryParseMessage(ref buffer, out _);
|
LengthPrefixedTextMessageParser.TryParseMessage(ref buffer, out _);
|
||||||
});
|
});
|
||||||
Assert.Equal(expectedMessage, ex.Message);
|
Assert.Equal(expectedMessage, ex.Message);
|
||||||
}
|
}
|
||||||
|
|
@ -83,7 +82,7 @@ namespace Microsoft.AspNetCore.Sockets.Common.Tests.Internal.Formatters
|
||||||
ReadOnlyBuffer<byte> buffer = new byte[] { 0x48, 0x65, 0x80, 0x6C, 0x6F, (byte)':' };
|
ReadOnlyBuffer<byte> buffer = new byte[] { 0x48, 0x65, 0x80, 0x6C, 0x6F, (byte)':' };
|
||||||
var ex = Assert.Throws<FormatException>(() =>
|
var ex = Assert.Throws<FormatException>(() =>
|
||||||
{
|
{
|
||||||
TextMessageParser.TryParseMessage(ref buffer, out _);
|
LengthPrefixedTextMessageParser.TryParseMessage(ref buffer, out _);
|
||||||
});
|
});
|
||||||
Assert.Equal("Invalid length: 'He<48>lo'", ex.Message);
|
Assert.Equal("Invalid length: 'He<48>lo'", ex.Message);
|
||||||
}
|
}
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.AspNetCore.Sockets.Internal.Formatters;
|
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Sockets.Tests.Internal.Formatters
|
namespace Microsoft.AspNetCore.Sockets.Tests.Internal.Formatters
|
||||||
|
|
@ -2,13 +2,12 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.AspNetCore.Sockets.Internal.Formatters;
|
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Sockets.Common.Tests.Internal.Formatters
|
namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Formatters
|
||||||
{
|
{
|
||||||
public class BinaryMessageParserTests
|
public class BinaryMessageParserTests
|
||||||
{
|
{
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
// 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.IO;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Formatters
|
||||||
|
{
|
||||||
|
public class TextMessageFormatterTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void WriteMessage()
|
||||||
|
{
|
||||||
|
using (var ms = new MemoryStream())
|
||||||
|
{
|
||||||
|
TextMessageFormatter.WriteMessage(new ReadOnlySpan<byte>(Encoding.UTF8.GetBytes("ABC")), ms);
|
||||||
|
Assert.Equal("ABC\u001e", Encoding.UTF8.GetString(ms.ToArray()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
// 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.Text;
|
||||||
|
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Formatters
|
||||||
|
{
|
||||||
|
public class TextMessageParserTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void ReadMessage()
|
||||||
|
{
|
||||||
|
var message = new ReadOnlyBuffer<byte>(Encoding.UTF8.GetBytes("ABC\u001e"));
|
||||||
|
|
||||||
|
Assert.True(TextMessageParser.TryParseMessage(ref message, out var payload));
|
||||||
|
Assert.Equal("ABC", Encoding.UTF8.GetString(payload.ToArray()));
|
||||||
|
Assert.False(TextMessageParser.TryParseMessage(ref message, out payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TryReadingIncompleteMessage()
|
||||||
|
{
|
||||||
|
var message = new ReadOnlyBuffer<byte>(Encoding.UTF8.GetBytes("ABC"));
|
||||||
|
Assert.False(TextMessageParser.TryParseMessage(ref message, out var payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TryReadingMultipleMessages()
|
||||||
|
{
|
||||||
|
var message = new ReadOnlyBuffer<byte>(Encoding.UTF8.GetBytes("ABC\u001eXYZ\u001e"));
|
||||||
|
Assert.True(TextMessageParser.TryParseMessage(ref message, out var payload));
|
||||||
|
Assert.Equal("ABC", Encoding.UTF8.GetString(payload.ToArray()));
|
||||||
|
Assert.True(TextMessageParser.TryParseMessage(ref message, out payload));
|
||||||
|
Assert.Equal("XYZ", Encoding.UTF8.GetString(payload.ToArray()));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IncompleteTrailingMessage()
|
||||||
|
{
|
||||||
|
var message = new ReadOnlyBuffer<byte>(Encoding.UTF8.GetBytes("ABC\u001eXYZ\u001e123"));
|
||||||
|
Assert.True(TextMessageParser.TryParseMessage(ref message, out var payload));
|
||||||
|
Assert.Equal("ABC", Encoding.UTF8.GetString(payload.ToArray()));
|
||||||
|
Assert.True(TextMessageParser.TryParseMessage(ref message, out payload));
|
||||||
|
Assert.Equal("XYZ", Encoding.UTF8.GetString(payload.ToArray()));
|
||||||
|
Assert.False(TextMessageParser.TryParseMessage(ref message, out payload));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,8 +5,8 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
|
||||||
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
||||||
using Microsoft.AspNetCore.Sockets.Internal.Formatters;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,12 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("2:", "Unable to parse payload as a negotiation message.")]
|
[InlineData("", "Unable to parse payload as a negotiation message.")]
|
||||||
[InlineData("2:42;", "Unexpected JSON Token Type 'Integer'. Expected a JSON Object.")]
|
[InlineData("42\u001e", "Unexpected JSON Token Type 'Integer'. Expected a JSON Object.")]
|
||||||
[InlineData("4:\"42\";", "Unexpected JSON Token Type 'String'. Expected a JSON Object.")]
|
[InlineData("\"42\"\u001e", "Unexpected JSON Token Type 'String'. Expected a JSON Object.")]
|
||||||
[InlineData("4:null;", "Unexpected JSON Token Type 'Null'. Expected a JSON Object.")]
|
[InlineData("null\u001e", "Unexpected JSON Token Type 'Null'. Expected a JSON Object.")]
|
||||||
[InlineData("2:{};", "Missing required property 'protocol'.")]
|
[InlineData("{}\u001e", "Missing required property 'protocol'.")]
|
||||||
[InlineData("2:[];", "Unexpected JSON Token Type 'Array'. Expected a JSON Object.")]
|
[InlineData("[]\u001e", "Unexpected JSON Token Type 'Array'. Expected a JSON Object.")]
|
||||||
public void ParsingNegotiationMessageThrowsForInvalidMessages(string payload, string expectedMessage)
|
public void ParsingNegotiationMessageThrowsForInvalidMessages(string payload, string expectedMessage)
|
||||||
{
|
{
|
||||||
var message = Encoding.UTF8.GetBytes(payload);
|
var message = Encoding.UTF8.GetBytes(payload);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using Microsoft.AspNetCore.Sockets.Internal.Formatters;
|
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
|
namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue