Skip to content

Commit f30e738

Browse files
Pre-encode known Json values in SignalR (dotnet#9999)
1 parent 1b47f44 commit f30e738

File tree

3 files changed

+43
-46
lines changed

3 files changed

+43
-46
lines changed

src/SignalR/common/Http.Connections.Common/src/NegotiateProtocol.cs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,22 @@ namespace Microsoft.AspNetCore.Http.Connections
1313
{
1414
public static class NegotiateProtocol
1515
{
16-
// Use C#7.3's ReadOnlySpan<byte> optimization for static data https://vcsjones.com/2019/02/01/csharp-readonly-span-bytes-static/
1716
private const string ConnectionIdPropertyName = "connectionId";
18-
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' };
17+
private static JsonEncodedText ConnectionIdPropertyNameBytes = JsonEncodedText.Encode(ConnectionIdPropertyName);
1918
private const string UrlPropertyName = "url";
20-
private static ReadOnlySpan<byte> UrlPropertyNameBytes => new byte[] { (byte)'u', (byte)'r', (byte)'l' };
19+
private static JsonEncodedText UrlPropertyNameBytes = JsonEncodedText.Encode(UrlPropertyName);
2120
private const string AccessTokenPropertyName = "accessToken";
22-
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' };
21+
private static JsonEncodedText AccessTokenPropertyNameBytes = JsonEncodedText.Encode(AccessTokenPropertyName);
2322
private const string AvailableTransportsPropertyName = "availableTransports";
24-
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' };
23+
private static JsonEncodedText AvailableTransportsPropertyNameBytes = JsonEncodedText.Encode(AvailableTransportsPropertyName);
2524
private const string TransportPropertyName = "transport";
26-
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' };
25+
private static JsonEncodedText TransportPropertyNameBytes = JsonEncodedText.Encode(TransportPropertyName);
2726
private const string TransferFormatsPropertyName = "transferFormats";
28-
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' };
27+
private static JsonEncodedText TransferFormatsPropertyNameBytes = JsonEncodedText.Encode(TransferFormatsPropertyName);
2928
private const string ErrorPropertyName = "error";
30-
private static ReadOnlySpan<byte> ErrorPropertyNameBytes => new byte[] { (byte)'e', (byte)'r', (byte)'r', (byte)'o', (byte)'r' };
29+
private static JsonEncodedText ErrorPropertyNameBytes = JsonEncodedText.Encode(ErrorPropertyName);
3130

31+
// Use C#7.3's ReadOnlySpan<byte> optimization for static data https://vcsjones.com/2019/02/01/csharp-readonly-span-bytes-static/
3232
// Used to detect ASP.NET SignalR Server connection attempt
3333
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' };
3434

@@ -120,19 +120,19 @@ public static NegotiationResponse ParseResponse(ReadOnlySpan<byte> content)
120120
switch (reader.TokenType)
121121
{
122122
case JsonTokenType.PropertyName:
123-
if (reader.TextEquals(UrlPropertyNameBytes))
123+
if (reader.TextEquals(UrlPropertyNameBytes.EncodedUtf8Bytes))
124124
{
125125
url = reader.ReadAsString(UrlPropertyName);
126126
}
127-
else if (reader.TextEquals(AccessTokenPropertyNameBytes))
127+
else if (reader.TextEquals(AccessTokenPropertyNameBytes.EncodedUtf8Bytes))
128128
{
129129
accessToken = reader.ReadAsString(AccessTokenPropertyName);
130130
}
131-
else if (reader.TextEquals(ConnectionIdPropertyNameBytes))
131+
else if (reader.TextEquals(ConnectionIdPropertyNameBytes.EncodedUtf8Bytes))
132132
{
133133
connectionId = reader.ReadAsString(ConnectionIdPropertyName);
134134
}
135-
else if (reader.TextEquals(AvailableTransportsPropertyNameBytes))
135+
else if (reader.TextEquals(AvailableTransportsPropertyNameBytes.EncodedUtf8Bytes))
136136
{
137137
reader.CheckRead();
138138
reader.EnsureArrayStart();
@@ -150,7 +150,7 @@ public static NegotiationResponse ParseResponse(ReadOnlySpan<byte> content)
150150
}
151151
}
152152
}
153-
else if (reader.TextEquals(ErrorPropertyNameBytes))
153+
else if (reader.TextEquals(ErrorPropertyNameBytes.EncodedUtf8Bytes))
154154
{
155155
error = reader.ReadAsString(ErrorPropertyName);
156156
}
@@ -221,11 +221,11 @@ private static AvailableTransport ParseAvailableTransport(ref Utf8JsonReader rea
221221
case JsonTokenType.PropertyName:
222222
var memberName = reader.ValueSpan;
223223

224-
if (memberName.SequenceEqual(TransportPropertyNameBytes))
224+
if (memberName.SequenceEqual(TransportPropertyNameBytes.EncodedUtf8Bytes))
225225
{
226226
availableTransport.Transport = reader.ReadAsString(TransportPropertyName);
227227
}
228-
else if (memberName.SequenceEqual(TransferFormatsPropertyNameBytes))
228+
else if (memberName.SequenceEqual(TransferFormatsPropertyNameBytes.EncodedUtf8Bytes))
229229
{
230230
reader.CheckRead();
231231
reader.EnsureArrayStart();

src/SignalR/common/Protocols.Json/src/Protocol/JsonHubProtocol.cs

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,24 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
2020
/// </summary>
2121
public sealed class JsonHubProtocol : IHubProtocol
2222
{
23-
// Use C#7.3's ReadOnlySpan<byte> optimization for static data https://vcsjones.com/2019/02/01/csharp-readonly-span-bytes-static/
2423
private const string ResultPropertyName = "result";
25-
private static ReadOnlySpan<byte> ResultPropertyNameBytes => new byte[] { (byte)'r', (byte)'e', (byte)'s', (byte)'u', (byte)'l', (byte)'t' };
24+
private static JsonEncodedText ResultPropertyNameBytes = JsonEncodedText.Encode(ResultPropertyName);
2625
private const string ItemPropertyName = "item";
27-
private static ReadOnlySpan<byte> ItemPropertyNameBytes => new byte[] { (byte)'i', (byte)'t', (byte)'e', (byte)'m' };
26+
private static JsonEncodedText ItemPropertyNameBytes = JsonEncodedText.Encode(ItemPropertyName);
2827
private const string InvocationIdPropertyName = "invocationId";
29-
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' };
28+
private static JsonEncodedText InvocationIdPropertyNameBytes = JsonEncodedText.Encode(InvocationIdPropertyName);
3029
private const string StreamIdsPropertyName = "streamIds";
31-
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' };
30+
private static JsonEncodedText StreamIdsPropertyNameBytes = JsonEncodedText.Encode(StreamIdsPropertyName);
3231
private const string TypePropertyName = "type";
33-
private static ReadOnlySpan<byte> TypePropertyNameBytes => new byte[] { (byte)'t', (byte)'y', (byte)'p', (byte)'e' };
32+
private static JsonEncodedText TypePropertyNameBytes = JsonEncodedText.Encode(TypePropertyName);
3433
private const string ErrorPropertyName = "error";
35-
private static ReadOnlySpan<byte> ErrorPropertyNameBytes => new byte[] { (byte)'e', (byte)'r', (byte)'r', (byte)'o', (byte)'r' };
34+
private static JsonEncodedText ErrorPropertyNameBytes = JsonEncodedText.Encode(ErrorPropertyName);
3635
private const string TargetPropertyName = "target";
37-
private static ReadOnlySpan<byte> TargetPropertyNameBytes => new byte[] { (byte)'t', (byte)'a', (byte)'r', (byte)'g', (byte)'e', (byte)'t' };
36+
private static JsonEncodedText TargetPropertyNameBytes = JsonEncodedText.Encode(TargetPropertyName);
3837
private const string ArgumentsPropertyName = "arguments";
39-
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' };
38+
private static JsonEncodedText ArgumentsPropertyNameBytes = JsonEncodedText.Encode(ArgumentsPropertyName);
4039
private const string HeadersPropertyName = "headers";
41-
private static ReadOnlySpan<byte> HeadersPropertyNameBytes => new byte[] { (byte)'h', (byte)'e', (byte)'a', (byte)'d', (byte)'e', (byte)'r', (byte)'s' };
40+
private static JsonEncodedText HeadersPropertyNameBytes = JsonEncodedText.Encode(HeadersPropertyName);
4241

4342
private static readonly string ProtocolName = "json";
4443
private static readonly int ProtocolVersion = 1;
@@ -144,7 +143,7 @@ private HubMessage ParseMessage(ReadOnlySequence<byte> input, IInvocationBinder
144143
switch (reader.TokenType)
145144
{
146145
case JsonTokenType.PropertyName:
147-
if (reader.TextEquals(TypePropertyNameBytes))
146+
if (reader.TextEquals(TypePropertyNameBytes.EncodedUtf8Bytes))
148147
{
149148
type = reader.ReadAsInt32(TypePropertyName);
150149

@@ -153,11 +152,11 @@ private HubMessage ParseMessage(ReadOnlySequence<byte> input, IInvocationBinder
153152
throw new InvalidDataException($"Expected '{TypePropertyName}' to be of type {JsonTokenType.Number}.");
154153
}
155154
}
156-
else if (reader.TextEquals(InvocationIdPropertyNameBytes))
155+
else if (reader.TextEquals(InvocationIdPropertyNameBytes.EncodedUtf8Bytes))
157156
{
158157
invocationId = reader.ReadAsString(InvocationIdPropertyName);
159158
}
160-
else if (reader.TextEquals(StreamIdsPropertyNameBytes))
159+
else if (reader.TextEquals(StreamIdsPropertyNameBytes.EncodedUtf8Bytes))
161160
{
162161
reader.CheckRead();
163162

@@ -177,15 +176,15 @@ private HubMessage ParseMessage(ReadOnlySequence<byte> input, IInvocationBinder
177176

178177
streamIds = newStreamIds.ToArray();
179178
}
180-
else if (reader.TextEquals(TargetPropertyNameBytes))
179+
else if (reader.TextEquals(TargetPropertyNameBytes.EncodedUtf8Bytes))
181180
{
182181
target = reader.ReadAsString(TargetPropertyName);
183182
}
184-
else if (reader.TextEquals(ErrorPropertyNameBytes))
183+
else if (reader.TextEquals(ErrorPropertyNameBytes.EncodedUtf8Bytes))
185184
{
186185
error = reader.ReadAsString(ErrorPropertyName);
187186
}
188-
else if (reader.TextEquals(ResultPropertyNameBytes))
187+
else if (reader.TextEquals(ResultPropertyNameBytes.EncodedUtf8Bytes))
189188
{
190189
hasResult = true;
191190

@@ -204,7 +203,7 @@ private HubMessage ParseMessage(ReadOnlySequence<byte> input, IInvocationBinder
204203
result = BindType(token.RootElement, returnType);
205204
}
206205
}
207-
else if (reader.TextEquals(ItemPropertyNameBytes))
206+
else if (reader.TextEquals(ItemPropertyNameBytes.EncodedUtf8Bytes))
208207
{
209208
reader.CheckRead();
210209

@@ -233,7 +232,7 @@ private HubMessage ParseMessage(ReadOnlySequence<byte> input, IInvocationBinder
233232
return new StreamBindingFailureMessage(id, ExceptionDispatchInfo.Capture(ex));
234233
}
235234
}
236-
else if (reader.TextEquals(ArgumentsPropertyNameBytes))
235+
else if (reader.TextEquals(ArgumentsPropertyNameBytes.EncodedUtf8Bytes))
237236
{
238237
reader.CheckRead();
239238

@@ -272,7 +271,7 @@ private HubMessage ParseMessage(ReadOnlySequence<byte> input, IInvocationBinder
272271
}
273272
}
274273
}
275-
else if (reader.TextEquals(HeadersPropertyNameBytes))
274+
else if (reader.TextEquals(HeadersPropertyNameBytes.EncodedUtf8Bytes))
276275
{
277276
reader.CheckRead();
278277
headers = ReadHeaders(ref reader);
@@ -516,7 +515,7 @@ private void WriteCompletionMessage(CompletionMessage message, Utf8JsonWriter wr
516515
else if (message.HasResult)
517516
{
518517
using var token = GetParsedObject(message.Result, message.Result?.GetType());
519-
token.RootElement.WriteAsProperty(ResultPropertyNameBytes, writer);
518+
token.RootElement.WriteAsProperty(ResultPropertyNameBytes.EncodedUtf8Bytes, writer);
520519
}
521520
}
522521

@@ -530,7 +529,7 @@ private void WriteStreamItemMessage(StreamItemMessage message, Utf8JsonWriter wr
530529
WriteInvocationId(message, writer);
531530

532531
using var token = GetParsedObject(message.Item, message.Item?.GetType());
533-
token.RootElement.WriteAsProperty(ItemPropertyNameBytes, writer);
532+
token.RootElement.WriteAsProperty(ItemPropertyNameBytes.EncodedUtf8Bytes, writer);
534533
}
535534

536535
private void WriteInvocationMessage(InvocationMessage message, Utf8JsonWriter writer)

src/SignalR/common/SignalR.Common/src/Protocol/HandshakeProtocol.cs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
using System.Text;
1010
using System.Text.Json;
1111
using Microsoft.AspNetCore.Internal;
12-
using Microsoft.AspNetCore.SignalR.Internal;
1312

1413
namespace Microsoft.AspNetCore.SignalR.Protocol
1514
{
@@ -18,15 +17,14 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
1817
/// </summary>
1918
public static class HandshakeProtocol
2019
{
21-
// Use C#7.3's ReadOnlySpan<byte> optimization for static data https://vcsjones.com/2019/02/01/csharp-readonly-span-bytes-static/
2220
private const string ProtocolPropertyName = "protocol";
23-
private static ReadOnlySpan<byte> ProtocolPropertyNameBytes => new byte[] { (byte)'p', (byte)'r', (byte)'o', (byte)'t', (byte)'o', (byte)'c', (byte)'o', (byte)'l' };
21+
private static JsonEncodedText ProtocolPropertyNameBytes = JsonEncodedText.Encode(ProtocolPropertyName);
2422
private const string ProtocolVersionPropertyName = "version";
25-
private static ReadOnlySpan<byte> ProtocolVersionPropertyNameBytes => new byte[] { (byte)'v', (byte)'e', (byte)'r', (byte)'s', (byte)'i', (byte)'o', (byte)'n' };
23+
private static JsonEncodedText ProtocolVersionPropertyNameBytes = JsonEncodedText.Encode(ProtocolVersionPropertyName);
2624
private const string ErrorPropertyName = "error";
27-
private static ReadOnlySpan<byte> ErrorPropertyNameBytes => new byte[] { (byte)'e', (byte)'r', (byte)'r', (byte)'o', (byte)'r' };
25+
private static JsonEncodedText ErrorPropertyNameBytes = JsonEncodedText.Encode(ErrorPropertyName);
2826
private const string TypePropertyName = "type";
29-
private static ReadOnlySpan<byte> TypePropertyNameBytes => new byte[] { (byte)'t', (byte)'y', (byte)'p', (byte)'e' };
27+
private static JsonEncodedText TypePropertyNameBytes = JsonEncodedText.Encode(TypePropertyName);
3028

3129
private static ConcurrentDictionary<IHubProtocol, ReadOnlyMemory<byte>> _messageCache = new ConcurrentDictionary<IHubProtocol, ReadOnlyMemory<byte>>();
3230

@@ -135,13 +133,13 @@ public static bool TryParseResponseMessage(ref ReadOnlySequence<byte> buffer, ou
135133
{
136134
if (reader.TokenType == JsonTokenType.PropertyName)
137135
{
138-
if (reader.TextEquals(TypePropertyNameBytes))
136+
if (reader.TextEquals(TypePropertyNameBytes.EncodedUtf8Bytes))
139137
{
140138
// a handshake response does not have a type
141139
// check the incoming message was not any other type of message
142140
throw new InvalidDataException("Expected a handshake response from the server.");
143141
}
144-
else if (reader.TextEquals(ErrorPropertyNameBytes))
142+
else if (reader.TextEquals(ErrorPropertyNameBytes.EncodedUtf8Bytes))
145143
{
146144
error = reader.ReadAsString(ErrorPropertyName);
147145
}
@@ -190,11 +188,11 @@ public static bool TryParseRequestMessage(ref ReadOnlySequence<byte> buffer, out
190188
{
191189
if (reader.TokenType == JsonTokenType.PropertyName)
192190
{
193-
if (reader.TextEquals(ProtocolPropertyNameBytes))
191+
if (reader.TextEquals(ProtocolPropertyNameBytes.EncodedUtf8Bytes))
194192
{
195193
protocol = reader.ReadAsString(ProtocolPropertyName);
196194
}
197-
else if (reader.TextEquals(ProtocolVersionPropertyNameBytes))
195+
else if (reader.TextEquals(ProtocolVersionPropertyNameBytes.EncodedUtf8Bytes))
198196
{
199197
protocolVersion = reader.ReadAsInt32(ProtocolVersionPropertyName);
200198
}

0 commit comments

Comments
 (0)