Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<Compile Include="$(SignalRSharedSourceRoot)PipeWriterStream.cs" Link="PipeWriterStream.cs" />
<Compile Include="$(SignalRSharedSourceRoot)ReflectionHelper.cs" Link="ReflectionHelper.cs" />
<Compile Include="$(SignalRSharedSourceRoot)TimerAwaitable.cs" Link="Internal\TimerAwaitable.cs" />
<Compile Include="$(SignalRSharedSourceRoot)ValueTaskExtensions.cs" Link="Internal\ValueTaskExtensions.cs" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO instead of placing this code under SignalR's shared source directory, it should be under the repos shared source directory: src/Shared -> https://github.com/aspnet/AspNetCore/tree/master/src/Shared

That way Kestrel and SignalR can use the same copy of code. There is no overhead of creating a source package because we mono repo now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do it in another PR. I don't care that much.

</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ internal class ServerSentEventsMessageParser
private const byte ByteLF = (byte)'\n';
private const byte ByteColon = (byte)':';

private static readonly byte[] _dataPrefix = Encoding.UTF8.GetBytes("data: ");
private static readonly byte[] _sseLineEnding = Encoding.UTF8.GetBytes("\r\n");
private static ReadOnlySpan<byte> _dataPrefix => new byte[] { (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)':', (byte)' ' };
private static ReadOnlySpan<byte> _sseLineEnding => new byte[] { (byte)'\r', (byte)'\n' };
private static readonly byte[] _newLine = Encoding.UTF8.GetBytes(Environment.NewLine);

private InternalParseState _internalParserState = InternalParseState.ReadMessagePayload;
Expand Down
27 changes: 14 additions & 13 deletions src/SignalR/common/Http.Connections.Common/src/NegotiateProtocol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,25 @@ 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 readonly byte[] ConnectionIdPropertyNameBytes = Encoding.UTF8.GetBytes(ConnectionIdPropertyName);
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 const string UrlPropertyName = "url";
private static readonly byte[] UrlPropertyNameBytes = Encoding.UTF8.GetBytes(UrlPropertyName);
private static ReadOnlySpan<byte> UrlPropertyNameBytes => new byte[] { (byte)'u', (byte)'r', (byte)'l' };
private const string AccessTokenPropertyName = "accessToken";
private static readonly byte[] AccessTokenPropertyNameBytes = Encoding.UTF8.GetBytes(AccessTokenPropertyName);
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 const string AvailableTransportsPropertyName = "availableTransports";
private static readonly byte[] AvailableTransportsPropertyNameBytes = Encoding.UTF8.GetBytes(AvailableTransportsPropertyName);
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 const string TransportPropertyName = "transport";
private static readonly byte[] TransportPropertyNameBytes = Encoding.UTF8.GetBytes(TransportPropertyName);
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 const string TransferFormatsPropertyName = "transferFormats";
private static readonly byte[] TransferFormatsPropertyNameBytes = Encoding.UTF8.GetBytes(TransferFormatsPropertyName);
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 const string ErrorPropertyName = "error";
private static readonly byte[] ErrorPropertyNameBytes = Encoding.UTF8.GetBytes(ErrorPropertyName);
private static ReadOnlySpan<byte> ErrorPropertyNameBytes => new byte[] { (byte)'e', (byte)'r', (byte)'r', (byte)'o', (byte)'r' };

// Used to detect ASP.NET SignalR Server connection attempt
private const string ProtocolVersionPropertyName = "ProtocolVersion";
private static readonly byte[] ProtocolVersionPropertyNameBytes = Encoding.UTF8.GetBytes(ProtocolVersionPropertyName);
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' };

public static void WriteResponse(NegotiationResponse response, IBufferWriter<byte> output)
{
Expand Down Expand Up @@ -114,15 +115,15 @@ public static NegotiationResponse ParseResponse(ReadOnlySpan<byte> content)

if (memberName.SequenceEqual(UrlPropertyNameBytes))
{
url = reader.ReadAsString(UrlPropertyNameBytes);
url = reader.ReadAsString(UrlPropertyName);
}
else if (memberName.SequenceEqual(AccessTokenPropertyNameBytes))
{
accessToken = reader.ReadAsString(AccessTokenPropertyNameBytes);
accessToken = reader.ReadAsString(AccessTokenPropertyName);
}
else if (memberName.SequenceEqual(ConnectionIdPropertyNameBytes))
{
connectionId = reader.ReadAsString(ConnectionIdPropertyNameBytes);
connectionId = reader.ReadAsString(ConnectionIdPropertyName);
}
else if (memberName.SequenceEqual(AvailableTransportsPropertyNameBytes))
{
Expand All @@ -144,7 +145,7 @@ public static NegotiationResponse ParseResponse(ReadOnlySpan<byte> content)
}
else if (memberName.SequenceEqual(ErrorPropertyNameBytes))
{
error = reader.ReadAsString(ErrorPropertyNameBytes);
error = reader.ReadAsString(ErrorPropertyName);
}
else if (memberName.SequenceEqual(ProtocolVersionPropertyNameBytes))
{
Expand Down Expand Up @@ -215,7 +216,7 @@ private static AvailableTransport ParseAvailableTransport(ref Utf8JsonReader rea

if (memberName.SequenceEqual(TransportPropertyNameBytes))
{
availableTransport.Transport = reader.ReadAsString(TransportPropertyNameBytes);
availableTransport.Transport = reader.ReadAsString(TransportPropertyName);
}
else if (memberName.SequenceEqual(TransferFormatsPropertyNameBytes))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<Compile Include="$(SignalRSharedSourceRoot)WebSocketExtensions.cs" Link="WebSocketExtensions.cs" />
<Compile Include="$(SignalRSharedSourceRoot)StreamExtensions.cs" Link="StreamExtensions.cs" />
<Compile Include="$(SignalRSharedSourceRoot)DuplexPipe.cs" Link="DuplexPipe.cs" />
<Compile Include="$(SignalRSharedSourceRoot)ValueTaskExtensions.cs" Link="ValueTaskExtensions.cs" />
</ItemGroup>

<ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions src/SignalR/common/Shared/PipeWriterStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Internal;

namespace System.IO.Pipelines
{
Expand Down
9 changes: 5 additions & 4 deletions src/SignalR/common/Shared/SystemTextJsonExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 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 System.Text.Json;
Expand Down Expand Up @@ -70,19 +71,19 @@ public static void Skip(this ref Utf8JsonReader reader)
}
}

public static string ReadAsString(this ref Utf8JsonReader reader, byte[] propertyName)
public static string ReadAsString(this ref Utf8JsonReader reader, string propertyName)
{
reader.Read();

if (reader.TokenType != JsonTokenType.String)
{
throw new InvalidDataException($"Expected '{Encoding.UTF8.GetString(propertyName)}' to be of type {JsonTokenType.String}.");
throw new InvalidDataException($"Expected '{propertyName}' to be of type {JsonTokenType.String}.");
}

return reader.GetString();
}

public static int? ReadAsInt32(this ref Utf8JsonReader reader, byte[] propertyName)
public static int? ReadAsInt32(this ref Utf8JsonReader reader, string propertyName)
{
reader.Read();

Expand All @@ -93,7 +94,7 @@ public static string ReadAsString(this ref Utf8JsonReader reader, byte[] propert

if (reader.TokenType != JsonTokenType.Number)
{
throw new InvalidDataException($"Expected '{Encoding.UTF8.GetString(propertyName)}' to be of type {JsonTokenType.Number}.");
throw new InvalidDataException($"Expected '{propertyName}' to be of type {JsonTokenType.Number}.");
}

return reader.GetInt32();
Expand Down
28 changes: 28 additions & 0 deletions src/SignalR/common/Shared/ValueTaskExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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.IO.Pipelines;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

namespace Microsoft.AspNetCore.Internal
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a fan of this namespace. Is this appropriate?

{
internal static class ValueTaskExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Task GetAsTask(this in ValueTask<FlushResult> valueTask)
{
// Try to avoid the allocation from AsTask
if (valueTask.IsCompletedSuccessfully)
{
// Signal consumption to the IValueTaskSource
valueTask.GetAwaiter().GetResult();
return Task.CompletedTask;
}
else
{
return valueTask.AsTask();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,17 @@ 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 readonly byte[] ProtocolPropertyNameBytes = Encoding.UTF8.GetBytes(ProtocolPropertyName);
private static ReadOnlySpan<byte> ProtocolPropertyNameBytes => new byte[] { (byte)'p', (byte)'r', (byte)'o', (byte)'t', (byte)'o', (byte)'c', (byte)'o', (byte)'l' };
private const string ProtocolVersionPropertyName = "version";
private static readonly byte[] ProtocolVersionPropertyNameBytes = Encoding.UTF8.GetBytes(ProtocolVersionPropertyName);
private static ReadOnlySpan<byte> ProtocolVersionPropertyNameBytes => new byte[] { (byte)'v', (byte)'e', (byte)'r', (byte)'s', (byte)'i', (byte)'o', (byte)'n' };
private const string MinorVersionPropertyName = "minorVersion";
private static readonly byte[] MinorVersionPropertyNameBytes = Encoding.UTF8.GetBytes(MinorVersionPropertyName);
private static ReadOnlySpan<byte> MinorVersionPropertyNameBytes => new byte[] { (byte)'m', (byte)'i', (byte)'n', (byte)'o', (byte)'r', (byte)'V', (byte)'e', (byte)'r', (byte)'s', (byte)'i', (byte)'o', (byte)'n' };
private const string ErrorPropertyName = "error";
private static readonly byte[] ErrorPropertyNameBytes = Encoding.UTF8.GetBytes(ErrorPropertyName);
private static ReadOnlySpan<byte> ErrorPropertyNameBytes => new byte[] { (byte)'e', (byte)'r', (byte)'r', (byte)'o', (byte)'r' };
private const string TypePropertyName = "type";
private static readonly byte[] TypePropertyNameBytes = Encoding.UTF8.GetBytes(TypePropertyName);
private static ReadOnlySpan<byte> TypePropertyNameBytes => new byte[] { (byte)'t', (byte)'y', (byte)'p', (byte)'e' };

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

Expand Down Expand Up @@ -128,11 +129,11 @@ public static bool TryParseResponseMessage(ref ReadOnlySequence<byte> buffer, ou
}
else if (memberName.SequenceEqual(ErrorPropertyNameBytes))
{
error = reader.ReadAsString(ErrorPropertyNameBytes);
error = reader.ReadAsString(ErrorPropertyName);
}
else if (memberName.SequenceEqual(MinorVersionPropertyNameBytes))
{
minorVersion = reader.ReadAsInt32(MinorVersionPropertyNameBytes);
minorVersion = reader.ReadAsInt32(MinorVersionPropertyName);
}
else
{
Expand Down Expand Up @@ -183,11 +184,11 @@ public static bool TryParseRequestMessage(ref ReadOnlySequence<byte> buffer, out

if (memberName.SequenceEqual(ProtocolPropertyNameBytes))
{
protocol = reader.ReadAsString(ProtocolPropertyNameBytes);
protocol = reader.ReadAsString(ProtocolPropertyName);
}
else if (memberName.SequenceEqual(ProtocolVersionPropertyNameBytes))
{
protocolVersion = reader.ReadAsInt32(ProtocolVersionPropertyNameBytes);
protocolVersion = reader.ReadAsInt32(ProtocolVersionPropertyName);
}
else
{
Expand Down
1 change: 1 addition & 0 deletions src/SignalR/server/Core/src/StreamTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Reflection;
using System.Threading.Channels;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.SignalR.Protocol;

namespace Microsoft.AspNetCore.SignalR
Expand Down