Pre-encode known Json values in SignalR (#9999)

This commit is contained in:
BrennanConroy 2019-05-07 15:21:00 -07:00 committed by GitHub
parent 1b47f44876
commit f30e738848
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 43 additions and 46 deletions

View File

@ -13,22 +13,22 @@ namespace Microsoft.AspNetCore.Http.Connections
{
public static class NegotiateProtocol
{
// Use C#7.3's ReadOnlySpan<byte> optimization for static data https://vcsjones.com/2019/02/01/csharp-readonly-span-bytes-static/
private const string ConnectionIdPropertyName = "connectionId";
private static ReadOnlySpan<byte> ConnectionIdPropertyNameBytes => new byte[] { (byte)'c', (byte)'o', (byte)'n', (byte)'n', (byte)'e', (byte)'c', (byte)'t', (byte)'i', (byte)'o', (byte)'n', (byte)'I', (byte)'d' };
private static JsonEncodedText ConnectionIdPropertyNameBytes = JsonEncodedText.Encode(ConnectionIdPropertyName);
private const string UrlPropertyName = "url";
private static ReadOnlySpan<byte> UrlPropertyNameBytes => new byte[] { (byte)'u', (byte)'r', (byte)'l' };
private static JsonEncodedText UrlPropertyNameBytes = JsonEncodedText.Encode(UrlPropertyName);
private const string AccessTokenPropertyName = "accessToken";
private static ReadOnlySpan<byte> AccessTokenPropertyNameBytes => new byte[] { (byte)'a', (byte)'c', (byte)'c', (byte)'e', (byte)'s', (byte)'s', (byte)'T', (byte)'o', (byte)'k', (byte)'e', (byte)'n' };
private static JsonEncodedText AccessTokenPropertyNameBytes = JsonEncodedText.Encode(AccessTokenPropertyName);
private const string AvailableTransportsPropertyName = "availableTransports";
private static ReadOnlySpan<byte> AvailableTransportsPropertyNameBytes => new byte[] { (byte)'a', (byte)'v', (byte)'a', (byte)'i', (byte)'l', (byte)'a', (byte)'b', (byte)'l', (byte)'e', (byte)'T', (byte)'r', (byte)'a', (byte)'n', (byte)'s', (byte)'p', (byte)'o', (byte)'r', (byte)'t', (byte)'s' };
private static JsonEncodedText AvailableTransportsPropertyNameBytes = JsonEncodedText.Encode(AvailableTransportsPropertyName);
private const string TransportPropertyName = "transport";
private static ReadOnlySpan<byte> TransportPropertyNameBytes => new byte[] { (byte)'t', (byte)'r', (byte)'a', (byte)'n', (byte)'s', (byte)'p', (byte)'o', (byte)'r', (byte)'t' };
private static JsonEncodedText TransportPropertyNameBytes = JsonEncodedText.Encode(TransportPropertyName);
private const string TransferFormatsPropertyName = "transferFormats";
private static ReadOnlySpan<byte> TransferFormatsPropertyNameBytes => new byte[] { (byte)'t', (byte)'r', (byte)'a', (byte)'n', (byte)'s', (byte)'f', (byte)'e', (byte)'r', (byte)'F', (byte)'o', (byte)'r', (byte)'m', (byte)'a', (byte)'t', (byte)'s' };
private static JsonEncodedText TransferFormatsPropertyNameBytes = JsonEncodedText.Encode(TransferFormatsPropertyName);
private const string ErrorPropertyName = "error";
private static ReadOnlySpan<byte> ErrorPropertyNameBytes => new byte[] { (byte)'e', (byte)'r', (byte)'r', (byte)'o', (byte)'r' };
private static JsonEncodedText ErrorPropertyNameBytes = JsonEncodedText.Encode(ErrorPropertyName);
// Use C#7.3's ReadOnlySpan<byte> optimization for static data https://vcsjones.com/2019/02/01/csharp-readonly-span-bytes-static/
// Used to detect ASP.NET SignalR Server connection attempt
private static ReadOnlySpan<byte> ProtocolVersionPropertyNameBytes => new byte[] { (byte)'P', (byte)'r', (byte)'o', (byte)'t', (byte)'o', (byte)'c', (byte)'o', (byte)'l', (byte)'V', (byte)'e', (byte)'r', (byte)'s', (byte)'i', (byte)'o', (byte)'n' };
@ -120,19 +120,19 @@ namespace Microsoft.AspNetCore.Http.Connections
switch (reader.TokenType)
{
case JsonTokenType.PropertyName:
if (reader.TextEquals(UrlPropertyNameBytes))
if (reader.TextEquals(UrlPropertyNameBytes.EncodedUtf8Bytes))
{
url = reader.ReadAsString(UrlPropertyName);
}
else if (reader.TextEquals(AccessTokenPropertyNameBytes))
else if (reader.TextEquals(AccessTokenPropertyNameBytes.EncodedUtf8Bytes))
{
accessToken = reader.ReadAsString(AccessTokenPropertyName);
}
else if (reader.TextEquals(ConnectionIdPropertyNameBytes))
else if (reader.TextEquals(ConnectionIdPropertyNameBytes.EncodedUtf8Bytes))
{
connectionId = reader.ReadAsString(ConnectionIdPropertyName);
}
else if (reader.TextEquals(AvailableTransportsPropertyNameBytes))
else if (reader.TextEquals(AvailableTransportsPropertyNameBytes.EncodedUtf8Bytes))
{
reader.CheckRead();
reader.EnsureArrayStart();
@ -150,7 +150,7 @@ namespace Microsoft.AspNetCore.Http.Connections
}
}
}
else if (reader.TextEquals(ErrorPropertyNameBytes))
else if (reader.TextEquals(ErrorPropertyNameBytes.EncodedUtf8Bytes))
{
error = reader.ReadAsString(ErrorPropertyName);
}
@ -221,11 +221,11 @@ namespace Microsoft.AspNetCore.Http.Connections
case JsonTokenType.PropertyName:
var memberName = reader.ValueSpan;
if (memberName.SequenceEqual(TransportPropertyNameBytes))
if (memberName.SequenceEqual(TransportPropertyNameBytes.EncodedUtf8Bytes))
{
availableTransport.Transport = reader.ReadAsString(TransportPropertyName);
}
else if (memberName.SequenceEqual(TransferFormatsPropertyNameBytes))
else if (memberName.SequenceEqual(TransferFormatsPropertyNameBytes.EncodedUtf8Bytes))
{
reader.CheckRead();
reader.EnsureArrayStart();

View File

@ -20,25 +20,24 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
/// </summary>
public sealed class JsonHubProtocol : IHubProtocol
{
// Use C#7.3's ReadOnlySpan<byte> optimization for static data https://vcsjones.com/2019/02/01/csharp-readonly-span-bytes-static/
private const string ResultPropertyName = "result";
private static ReadOnlySpan<byte> ResultPropertyNameBytes => new byte[] { (byte)'r', (byte)'e', (byte)'s', (byte)'u', (byte)'l', (byte)'t' };
private static JsonEncodedText ResultPropertyNameBytes = JsonEncodedText.Encode(ResultPropertyName);
private const string ItemPropertyName = "item";
private static ReadOnlySpan<byte> ItemPropertyNameBytes => new byte[] { (byte)'i', (byte)'t', (byte)'e', (byte)'m' };
private static JsonEncodedText ItemPropertyNameBytes = JsonEncodedText.Encode(ItemPropertyName);
private const string InvocationIdPropertyName = "invocationId";
private static ReadOnlySpan<byte> InvocationIdPropertyNameBytes => new byte[] { (byte)'i', (byte)'n', (byte)'v', (byte)'o', (byte)'c', (byte)'a', (byte)'t', (byte)'i', (byte)'o', (byte)'n', (byte)'I', (byte)'d' };
private static JsonEncodedText InvocationIdPropertyNameBytes = JsonEncodedText.Encode(InvocationIdPropertyName);
private const string StreamIdsPropertyName = "streamIds";
private static ReadOnlySpan<byte> StreamIdsPropertyNameBytes => new byte[] { (byte)'s', (byte)'t', (byte)'r', (byte)'e', (byte)'a', (byte)'m', (byte)'I', (byte)'d', (byte)'s' };
private static JsonEncodedText StreamIdsPropertyNameBytes = JsonEncodedText.Encode(StreamIdsPropertyName);
private const string TypePropertyName = "type";
private static ReadOnlySpan<byte> TypePropertyNameBytes => new byte[] { (byte)'t', (byte)'y', (byte)'p', (byte)'e' };
private static JsonEncodedText TypePropertyNameBytes = JsonEncodedText.Encode(TypePropertyName);
private const string ErrorPropertyName = "error";
private static ReadOnlySpan<byte> ErrorPropertyNameBytes => new byte[] { (byte)'e', (byte)'r', (byte)'r', (byte)'o', (byte)'r' };
private static JsonEncodedText ErrorPropertyNameBytes = JsonEncodedText.Encode(ErrorPropertyName);
private const string TargetPropertyName = "target";
private static ReadOnlySpan<byte> TargetPropertyNameBytes => new byte[] { (byte)'t', (byte)'a', (byte)'r', (byte)'g', (byte)'e', (byte)'t' };
private static JsonEncodedText TargetPropertyNameBytes = JsonEncodedText.Encode(TargetPropertyName);
private const string ArgumentsPropertyName = "arguments";
private static ReadOnlySpan<byte> ArgumentsPropertyNameBytes => new byte[] { (byte)'a', (byte)'r', (byte)'g', (byte)'u', (byte)'m', (byte)'e', (byte)'n', (byte)'t', (byte)'s' };
private static JsonEncodedText ArgumentsPropertyNameBytes = JsonEncodedText.Encode(ArgumentsPropertyName);
private const string HeadersPropertyName = "headers";
private static ReadOnlySpan<byte> HeadersPropertyNameBytes => new byte[] { (byte)'h', (byte)'e', (byte)'a', (byte)'d', (byte)'e', (byte)'r', (byte)'s' };
private static JsonEncodedText HeadersPropertyNameBytes = JsonEncodedText.Encode(HeadersPropertyName);
private static readonly string ProtocolName = "json";
private static readonly int ProtocolVersion = 1;
@ -144,7 +143,7 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
switch (reader.TokenType)
{
case JsonTokenType.PropertyName:
if (reader.TextEquals(TypePropertyNameBytes))
if (reader.TextEquals(TypePropertyNameBytes.EncodedUtf8Bytes))
{
type = reader.ReadAsInt32(TypePropertyName);
@ -153,11 +152,11 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
throw new InvalidDataException($"Expected '{TypePropertyName}' to be of type {JsonTokenType.Number}.");
}
}
else if (reader.TextEquals(InvocationIdPropertyNameBytes))
else if (reader.TextEquals(InvocationIdPropertyNameBytes.EncodedUtf8Bytes))
{
invocationId = reader.ReadAsString(InvocationIdPropertyName);
}
else if (reader.TextEquals(StreamIdsPropertyNameBytes))
else if (reader.TextEquals(StreamIdsPropertyNameBytes.EncodedUtf8Bytes))
{
reader.CheckRead();
@ -177,15 +176,15 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
streamIds = newStreamIds.ToArray();
}
else if (reader.TextEquals(TargetPropertyNameBytes))
else if (reader.TextEquals(TargetPropertyNameBytes.EncodedUtf8Bytes))
{
target = reader.ReadAsString(TargetPropertyName);
}
else if (reader.TextEquals(ErrorPropertyNameBytes))
else if (reader.TextEquals(ErrorPropertyNameBytes.EncodedUtf8Bytes))
{
error = reader.ReadAsString(ErrorPropertyName);
}
else if (reader.TextEquals(ResultPropertyNameBytes))
else if (reader.TextEquals(ResultPropertyNameBytes.EncodedUtf8Bytes))
{
hasResult = true;
@ -204,7 +203,7 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
result = BindType(token.RootElement, returnType);
}
}
else if (reader.TextEquals(ItemPropertyNameBytes))
else if (reader.TextEquals(ItemPropertyNameBytes.EncodedUtf8Bytes))
{
reader.CheckRead();
@ -233,7 +232,7 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
return new StreamBindingFailureMessage(id, ExceptionDispatchInfo.Capture(ex));
}
}
else if (reader.TextEquals(ArgumentsPropertyNameBytes))
else if (reader.TextEquals(ArgumentsPropertyNameBytes.EncodedUtf8Bytes))
{
reader.CheckRead();
@ -272,7 +271,7 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
}
}
}
else if (reader.TextEquals(HeadersPropertyNameBytes))
else if (reader.TextEquals(HeadersPropertyNameBytes.EncodedUtf8Bytes))
{
reader.CheckRead();
headers = ReadHeaders(ref reader);
@ -516,7 +515,7 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
else if (message.HasResult)
{
using var token = GetParsedObject(message.Result, message.Result?.GetType());
token.RootElement.WriteAsProperty(ResultPropertyNameBytes, writer);
token.RootElement.WriteAsProperty(ResultPropertyNameBytes.EncodedUtf8Bytes, writer);
}
}
@ -530,7 +529,7 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
WriteInvocationId(message, writer);
using var token = GetParsedObject(message.Item, message.Item?.GetType());
token.RootElement.WriteAsProperty(ItemPropertyNameBytes, writer);
token.RootElement.WriteAsProperty(ItemPropertyNameBytes.EncodedUtf8Bytes, writer);
}
private void WriteInvocationMessage(InvocationMessage message, Utf8JsonWriter writer)

View File

@ -9,7 +9,6 @@ using System.IO;
using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.SignalR.Internal;
namespace Microsoft.AspNetCore.SignalR.Protocol
{
@ -18,15 +17,14 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
/// </summary>
public static class HandshakeProtocol
{
// Use C#7.3's ReadOnlySpan<byte> optimization for static data https://vcsjones.com/2019/02/01/csharp-readonly-span-bytes-static/
private const string ProtocolPropertyName = "protocol";
private static ReadOnlySpan<byte> ProtocolPropertyNameBytes => new byte[] { (byte)'p', (byte)'r', (byte)'o', (byte)'t', (byte)'o', (byte)'c', (byte)'o', (byte)'l' };
private static JsonEncodedText ProtocolPropertyNameBytes = JsonEncodedText.Encode(ProtocolPropertyName);
private const string ProtocolVersionPropertyName = "version";
private static ReadOnlySpan<byte> ProtocolVersionPropertyNameBytes => new byte[] { (byte)'v', (byte)'e', (byte)'r', (byte)'s', (byte)'i', (byte)'o', (byte)'n' };
private static JsonEncodedText ProtocolVersionPropertyNameBytes = JsonEncodedText.Encode(ProtocolVersionPropertyName);
private const string ErrorPropertyName = "error";
private static ReadOnlySpan<byte> ErrorPropertyNameBytes => new byte[] { (byte)'e', (byte)'r', (byte)'r', (byte)'o', (byte)'r' };
private static JsonEncodedText ErrorPropertyNameBytes = JsonEncodedText.Encode(ErrorPropertyName);
private const string TypePropertyName = "type";
private static ReadOnlySpan<byte> TypePropertyNameBytes => new byte[] { (byte)'t', (byte)'y', (byte)'p', (byte)'e' };
private static JsonEncodedText TypePropertyNameBytes = JsonEncodedText.Encode(TypePropertyName);
private static ConcurrentDictionary<IHubProtocol, ReadOnlyMemory<byte>> _messageCache = new ConcurrentDictionary<IHubProtocol, ReadOnlyMemory<byte>>();
@ -135,13 +133,13 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
{
if (reader.TokenType == JsonTokenType.PropertyName)
{
if (reader.TextEquals(TypePropertyNameBytes))
if (reader.TextEquals(TypePropertyNameBytes.EncodedUtf8Bytes))
{
// a handshake response does not have a type
// check the incoming message was not any other type of message
throw new InvalidDataException("Expected a handshake response from the server.");
}
else if (reader.TextEquals(ErrorPropertyNameBytes))
else if (reader.TextEquals(ErrorPropertyNameBytes.EncodedUtf8Bytes))
{
error = reader.ReadAsString(ErrorPropertyName);
}
@ -190,11 +188,11 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
{
if (reader.TokenType == JsonTokenType.PropertyName)
{
if (reader.TextEquals(ProtocolPropertyNameBytes))
if (reader.TextEquals(ProtocolPropertyNameBytes.EncodedUtf8Bytes))
{
protocol = reader.ReadAsString(ProtocolPropertyName);
}
else if (reader.TextEquals(ProtocolVersionPropertyNameBytes))
else if (reader.TextEquals(ProtocolVersionPropertyNameBytes.EncodedUtf8Bytes))
{
protocolVersion = reader.ReadAsInt32(ProtocolVersionPropertyName);
}