Serialize NegotiateResponse with IBufferWriter (#1881)
This commit is contained in:
parent
f632330d7f
commit
acc0b7ad0d
|
|
@ -11,6 +11,7 @@ using BenchmarkDotNet.Attributes;
|
|||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.SignalR.Core;
|
||||
using Microsoft.AspNetCore.SignalR.Internal;
|
||||
using Microsoft.AspNetCore.Internal;
|
||||
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
||||
using Microsoft.AspNetCore.SignalR.Microbenchmarks.Shared;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ using BenchmarkDotNet.Attributes;
|
|||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Connections.Features;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Microsoft.AspNetCore.SignalR.Internal;
|
||||
using Microsoft.AspNetCore.Internal;
|
||||
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
||||
using Microsoft.AspNetCore.SignalR.Microbenchmarks.Shared;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ using Microsoft.AspNetCore.Connections;
|
|||
using Microsoft.AspNetCore.Connections.Features;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Microsoft.AspNetCore.SignalR.Internal;
|
||||
using Microsoft.AspNetCore.Internal;
|
||||
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
||||
using Microsoft.AspNetCore.SignalR.Microbenchmarks.Shared;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ using System;
|
|||
using System.Buffers;
|
||||
using System.IO;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Microsoft.AspNetCore.SignalR.Internal;
|
||||
using Microsoft.AspNetCore.Internal;
|
||||
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
|
||||
|
||||
namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Microsoft.AspNetCore.Http.Connections.Internal;
|
||||
using Microsoft.AspNetCore.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
|
||||
{
|
||||
public class NegotiateProtocolBenchmark
|
||||
{
|
||||
private NegotiationResponse _negotiateResponse;
|
||||
private Stream _stream;
|
||||
|
||||
[GlobalSetup]
|
||||
public void GlobalSetup()
|
||||
{
|
||||
_negotiateResponse = new NegotiationResponse
|
||||
{
|
||||
ConnectionId = "d100338e-8c01-4281-92c2-9a967fdeebcb",
|
||||
AvailableTransports = new List<AvailableTransport>
|
||||
{
|
||||
new AvailableTransport
|
||||
{
|
||||
Transport = "WebSockets",
|
||||
TransferFormats = new List<string>
|
||||
{
|
||||
"Text",
|
||||
"Binary"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
_stream = Stream.Null;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public Task WriteResponse_MemoryBufferWriter()
|
||||
{
|
||||
var writer = new MemoryBufferWriter();
|
||||
try
|
||||
{
|
||||
NegotiateProtocol.WriteResponse(_negotiateResponse, writer);
|
||||
return writer.CopyToAsync(_stream);
|
||||
}
|
||||
finally
|
||||
{
|
||||
writer.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Internal
|
||||
{
|
||||
internal sealed class MemoryBufferWriter : IBufferWriter<byte>
|
||||
{
|
||||
[ThreadStatic]
|
||||
private static MemoryBufferWriter _cachedInstance;
|
||||
|
||||
#if DEBUG
|
||||
private bool _inUse;
|
||||
#endif
|
||||
|
||||
private readonly int _segmentSize;
|
||||
private int _bytesWritten;
|
||||
|
||||
private List<byte[]> _fullSegments;
|
||||
private byte[] _currentSegment;
|
||||
private int _position;
|
||||
|
||||
public MemoryBufferWriter(int segmentSize = 2048)
|
||||
{
|
||||
_segmentSize = segmentSize;
|
||||
}
|
||||
|
||||
public int Length => _bytesWritten;
|
||||
|
||||
public static MemoryBufferWriter Get()
|
||||
{
|
||||
var writer = _cachedInstance;
|
||||
if (writer == null)
|
||||
{
|
||||
writer = new MemoryBufferWriter();
|
||||
}
|
||||
|
||||
// Taken off the thread static
|
||||
_cachedInstance = null;
|
||||
#if DEBUG
|
||||
if (writer._inUse)
|
||||
{
|
||||
throw new InvalidOperationException("The reader wasn't returned!");
|
||||
}
|
||||
|
||||
writer._inUse = true;
|
||||
#endif
|
||||
|
||||
return writer;
|
||||
}
|
||||
|
||||
public static void Return(MemoryBufferWriter writer)
|
||||
{
|
||||
_cachedInstance = writer;
|
||||
#if DEBUG
|
||||
writer._inUse = false;
|
||||
#endif
|
||||
writer.Reset();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
if (_fullSegments != null)
|
||||
{
|
||||
for (var i = 0; i < _fullSegments.Count; i++)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(_fullSegments[i]);
|
||||
}
|
||||
|
||||
_fullSegments.Clear();
|
||||
}
|
||||
|
||||
if (_currentSegment != null)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(_currentSegment);
|
||||
_currentSegment = null;
|
||||
}
|
||||
|
||||
_bytesWritten = 0;
|
||||
_position = 0;
|
||||
}
|
||||
|
||||
public void Advance(int count)
|
||||
{
|
||||
_bytesWritten += count;
|
||||
_position += count;
|
||||
}
|
||||
|
||||
public Memory<byte> GetMemory(int sizeHint = 0)
|
||||
{
|
||||
// TODO: Use sizeHint
|
||||
if (_currentSegment == null)
|
||||
{
|
||||
_currentSegment = ArrayPool<byte>.Shared.Rent(_segmentSize);
|
||||
_position = 0;
|
||||
}
|
||||
else if (_position == _segmentSize)
|
||||
{
|
||||
if (_fullSegments == null)
|
||||
{
|
||||
_fullSegments = new List<byte[]>();
|
||||
}
|
||||
_fullSegments.Add(_currentSegment);
|
||||
_currentSegment = ArrayPool<byte>.Shared.Rent(_segmentSize);
|
||||
_position = 0;
|
||||
}
|
||||
|
||||
return _currentSegment.AsMemory(_position, _currentSegment.Length - _position);
|
||||
}
|
||||
|
||||
public Span<byte> GetSpan(int sizeHint = 0)
|
||||
{
|
||||
return GetMemory(sizeHint).Span;
|
||||
}
|
||||
|
||||
public Task CopyToAsync(Stream stream)
|
||||
{
|
||||
if (_fullSegments == null)
|
||||
{
|
||||
// There is only one segment so write without async
|
||||
return stream.WriteAsync(_currentSegment, 0, _position);
|
||||
}
|
||||
|
||||
return CopyToSlowAsync(stream);
|
||||
}
|
||||
|
||||
private async Task CopyToSlowAsync(Stream stream)
|
||||
{
|
||||
if (_fullSegments != null)
|
||||
{
|
||||
// Copy full segments
|
||||
for (var i = 0; i < _fullSegments.Count - 1; i++)
|
||||
{
|
||||
await stream.WriteAsync(_fullSegments[i], 0, _segmentSize);
|
||||
}
|
||||
}
|
||||
|
||||
await stream.WriteAsync(_currentSegment, 0, _position);
|
||||
}
|
||||
|
||||
public byte[] ToArray()
|
||||
{
|
||||
if (_currentSegment == null)
|
||||
{
|
||||
return Array.Empty<byte>();
|
||||
}
|
||||
|
||||
var result = new byte[_bytesWritten];
|
||||
|
||||
var totalWritten = 0;
|
||||
|
||||
if (_fullSegments != null)
|
||||
{
|
||||
// Copy full segments
|
||||
for (var i = 0; i < _fullSegments.Count; i++)
|
||||
{
|
||||
_fullSegments[i].CopyTo(result, totalWritten);
|
||||
|
||||
totalWritten += _segmentSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy current incomplete segment
|
||||
_currentSegment.AsSpan(0, _position).CopyTo(result.AsSpan(totalWritten));
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ using System.Text;
|
|||
|
||||
namespace Microsoft.AspNetCore.SignalR.Internal
|
||||
{
|
||||
public class Utf8BufferTextReader : TextReader
|
||||
internal sealed class Utf8BufferTextReader : TextReader
|
||||
{
|
||||
private readonly Decoder _decoder;
|
||||
private ReadOnlySequence<byte> _utf8Buffer;
|
||||
|
|
@ -9,9 +9,9 @@ using System.Runtime.CompilerServices;
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.AspNetCore.SignalR.Internal
|
||||
namespace Microsoft.AspNetCore.Internal
|
||||
{
|
||||
public sealed class Utf8BufferTextWriter : TextWriter
|
||||
internal sealed class Utf8BufferTextWriter : TextWriter
|
||||
{
|
||||
private static readonly UTF8Encoding _utf8NoBom = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
|
||||
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
|
@ -12,44 +13,50 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
|
|||
{
|
||||
public static class NegotiateProtocol
|
||||
{
|
||||
private static readonly UTF8Encoding _utf8NoBom = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
|
||||
|
||||
private const string ConnectionIdPropertyName = "connectionId";
|
||||
private const string AvailableTransportsPropertyName = "availableTransports";
|
||||
private const string TransportPropertyName = "transport";
|
||||
private const string TransferFormatsPropertyName = "transferFormats";
|
||||
|
||||
public static void WriteResponse(NegotiationResponse response, Stream output)
|
||||
public static void WriteResponse(NegotiationResponse response, IBufferWriter<byte> output)
|
||||
{
|
||||
using (var jsonWriter = JsonUtils.CreateJsonTextWriter(new StreamWriter(output, _utf8NoBom, 1024, leaveOpen: true)))
|
||||
var textWriter = Utf8BufferTextWriter.Get(output);
|
||||
try
|
||||
{
|
||||
jsonWriter.WriteStartObject();
|
||||
jsonWriter.WritePropertyName(ConnectionIdPropertyName);
|
||||
jsonWriter.WriteValue(response.ConnectionId);
|
||||
jsonWriter.WritePropertyName(AvailableTransportsPropertyName);
|
||||
jsonWriter.WriteStartArray();
|
||||
|
||||
foreach (var availableTransport in response.AvailableTransports)
|
||||
using (var jsonWriter = JsonUtils.CreateJsonTextWriter(textWriter))
|
||||
{
|
||||
jsonWriter.WriteStartObject();
|
||||
jsonWriter.WritePropertyName(TransportPropertyName);
|
||||
jsonWriter.WriteValue(availableTransport.Transport);
|
||||
jsonWriter.WritePropertyName(TransferFormatsPropertyName);
|
||||
jsonWriter.WritePropertyName(ConnectionIdPropertyName);
|
||||
jsonWriter.WriteValue(response.ConnectionId);
|
||||
jsonWriter.WritePropertyName(AvailableTransportsPropertyName);
|
||||
jsonWriter.WriteStartArray();
|
||||
|
||||
foreach (var transferFormat in availableTransport.TransferFormats)
|
||||
foreach (var availableTransport in response.AvailableTransports)
|
||||
{
|
||||
jsonWriter.WriteValue(transferFormat);
|
||||
jsonWriter.WriteStartObject();
|
||||
jsonWriter.WritePropertyName(TransportPropertyName);
|
||||
jsonWriter.WriteValue(availableTransport.Transport);
|
||||
jsonWriter.WritePropertyName(TransferFormatsPropertyName);
|
||||
jsonWriter.WriteStartArray();
|
||||
|
||||
foreach (var transferFormat in availableTransport.TransferFormats)
|
||||
{
|
||||
jsonWriter.WriteValue(transferFormat);
|
||||
}
|
||||
|
||||
jsonWriter.WriteEndArray();
|
||||
jsonWriter.WriteEndObject();
|
||||
}
|
||||
|
||||
jsonWriter.WriteEndArray();
|
||||
jsonWriter.WriteEndObject();
|
||||
|
||||
jsonWriter.Flush();
|
||||
}
|
||||
|
||||
jsonWriter.WriteEndArray();
|
||||
jsonWriter.WriteEndObject();
|
||||
|
||||
jsonWriter.Flush();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Utf8BufferTextWriter.Return(textWriter);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@
|
|||
<Description>Common primitives for ASP.NET Connection Handlers and clients</Description>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<RootNamespace>Microsoft.AspNetCore.Http.Connections</RootNamespace>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Common\JsonUtils.cs" Link="Internal\JsonUtils.cs" />
|
||||
<Compile Include="..\Common\Utf8BufferTextWriter.cs" Link="Internal\Utf8BufferTextWriter.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="$(MicrosoftAspNetCoreConnectionsAbstractionsPackageVersion)" />
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
|
@ -14,6 +15,7 @@ using Microsoft.AspNetCore.Http.Connections.Features;
|
|||
using Microsoft.AspNetCore.Http.Connections.Internal;
|
||||
using Microsoft.AspNetCore.Http.Connections.Internal.Transports;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
|
|
@ -373,7 +375,7 @@ namespace Microsoft.AspNetCore.Http.Connections
|
|||
await connectionDelegate(connection);
|
||||
}
|
||||
|
||||
private Task ProcessNegotiate(HttpContext context, HttpConnectionOptions options, ConnectionLogScope logScope)
|
||||
private async Task ProcessNegotiate(HttpContext context, HttpConnectionOptions options, ConnectionLogScope logScope)
|
||||
{
|
||||
context.Response.ContentType = "application/json";
|
||||
|
||||
|
|
@ -384,17 +386,27 @@ namespace Microsoft.AspNetCore.Http.Connections
|
|||
// Connection ID metadata set.
|
||||
logScope.ConnectionId = connection.ConnectionId;
|
||||
|
||||
// Get the bytes for the connection id
|
||||
var negotiateResponseBuffer = GetNegotiatePayload(connection.ConnectionId, context, options);
|
||||
// Don't use thread static instance here because writer is used with async
|
||||
var writer = new MemoryBufferWriter();
|
||||
|
||||
Log.NegotiationRequest(_logger);
|
||||
try
|
||||
{
|
||||
// Get the bytes for the connection id
|
||||
WriteNegotiatePayload(writer, connection.ConnectionId, context, options);
|
||||
|
||||
// Write it out to the response with the right content length
|
||||
context.Response.ContentLength = negotiateResponseBuffer.Length;
|
||||
return context.Response.Body.WriteAsync(negotiateResponseBuffer, 0, negotiateResponseBuffer.Length);
|
||||
Log.NegotiationRequest(_logger);
|
||||
|
||||
// Write it out to the response with the right content length
|
||||
context.Response.ContentLength = writer.Length;
|
||||
await writer.CopyToAsync(context.Response.Body);
|
||||
}
|
||||
finally
|
||||
{
|
||||
writer.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] GetNegotiatePayload(string connectionId, HttpContext context, HttpConnectionOptions options)
|
||||
private static void WriteNegotiatePayload(IBufferWriter<byte> writer, string connectionId, HttpContext context, HttpConnectionOptions options)
|
||||
{
|
||||
var response = new NegotiationResponse();
|
||||
response.ConnectionId = connectionId;
|
||||
|
|
@ -415,10 +427,7 @@ namespace Microsoft.AspNetCore.Http.Connections
|
|||
response.AvailableTransports.Add(_longPollingAvailableTransport);
|
||||
}
|
||||
|
||||
var ms = new MemoryStream();
|
||||
NegotiateProtocol.WriteResponse(response, ms);
|
||||
|
||||
return ms.ToArray();
|
||||
NegotiateProtocol.WriteResponse(response, writer);
|
||||
}
|
||||
|
||||
private static bool ServerHasWebSockets(IFeatureCollection features)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Common\MemoryBufferWriter.cs" Link="MemoryBufferWriter.cs" />
|
||||
<Compile Include="..\Common\PipeWriterStream.cs" Link="PipeWriterStream.cs" />
|
||||
<Compile Include="..\Common\WebSocketExtensions.cs" Link="WebSocketExtensions.cs" />
|
||||
<Compile Include="..\Common\StreamExtensions.cs" Link="StreamExtensions.cs" />
|
||||
|
|
|
|||
|
|
@ -1,122 +0,0 @@
|
|||
// 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.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNetCore.SignalR.Internal
|
||||
{
|
||||
public sealed class MemoryBufferWriter : IBufferWriter<byte>
|
||||
{
|
||||
[ThreadStatic]
|
||||
private static MemoryBufferWriter _cachedInstance;
|
||||
|
||||
#if DEBUG
|
||||
private bool _inUse;
|
||||
#endif
|
||||
|
||||
private readonly int _segmentSize;
|
||||
private int _bytesWritten;
|
||||
|
||||
private List<byte[]> _segments;
|
||||
private int _position;
|
||||
|
||||
private MemoryBufferWriter(int segmentSize = 2048)
|
||||
{
|
||||
_segmentSize = segmentSize;
|
||||
|
||||
_segments = new List<byte[]>();
|
||||
}
|
||||
|
||||
public static MemoryBufferWriter Get()
|
||||
{
|
||||
var writer = _cachedInstance;
|
||||
if (writer == null)
|
||||
{
|
||||
writer = new MemoryBufferWriter();
|
||||
}
|
||||
|
||||
// Taken off the thread static
|
||||
_cachedInstance = null;
|
||||
#if DEBUG
|
||||
if (writer._inUse)
|
||||
{
|
||||
throw new InvalidOperationException("The reader wasn't returned!");
|
||||
}
|
||||
|
||||
writer._inUse = true;
|
||||
#endif
|
||||
|
||||
return writer;
|
||||
}
|
||||
|
||||
public static void Return(MemoryBufferWriter writer)
|
||||
{
|
||||
_cachedInstance = writer;
|
||||
#if DEBUG
|
||||
writer._inUse = false;
|
||||
#endif
|
||||
for (var i = 0; i < writer._segments.Count; i++)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(writer._segments[i]);
|
||||
}
|
||||
writer._segments.Clear();
|
||||
writer._bytesWritten = 0;
|
||||
writer._position = 0;
|
||||
}
|
||||
|
||||
public Memory<byte> CurrentSegment => _segments[_segments.Count - 1];
|
||||
|
||||
public void Advance(int count)
|
||||
{
|
||||
_bytesWritten += count;
|
||||
_position += count;
|
||||
}
|
||||
|
||||
public Memory<byte> GetMemory(int sizeHint = 0)
|
||||
{
|
||||
// TODO: Use sizeHint
|
||||
|
||||
if (_segments.Count == 0 || _position == _segmentSize)
|
||||
{
|
||||
_segments.Add(ArrayPool<byte>.Shared.Rent(_segmentSize));
|
||||
_position = 0;
|
||||
}
|
||||
|
||||
// Cache property access
|
||||
var currentSegment = CurrentSegment;
|
||||
return currentSegment.Slice(_position, currentSegment.Length - _position);
|
||||
}
|
||||
|
||||
public Span<byte> GetSpan(int sizeHint = 0)
|
||||
{
|
||||
return GetMemory(sizeHint).Span;
|
||||
}
|
||||
|
||||
public byte[] ToArray()
|
||||
{
|
||||
if (_segments.Count == 0)
|
||||
{
|
||||
return Array.Empty<byte>();
|
||||
}
|
||||
|
||||
var result = new byte[_bytesWritten];
|
||||
|
||||
var totalWritten = 0;
|
||||
|
||||
// Copy full segments
|
||||
for (var i = 0; i < _segments.Count - 1; i++)
|
||||
{
|
||||
_segments[i].AsMemory().CopyTo(result.AsMemory(totalWritten, _segmentSize));
|
||||
|
||||
totalWritten += _segmentSize;
|
||||
}
|
||||
|
||||
// Copy current incomplete segment
|
||||
CurrentSegment.Slice(0, _position).CopyTo(result.AsMemory(totalWritten, _position));
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.Buffers;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Internal;
|
||||
|
|
@ -16,6 +17,22 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
|
|||
private const string ErrorPropertyName = "error";
|
||||
private const string TypePropertyName = "type";
|
||||
|
||||
public static ReadOnlyMemory<byte> SuccessHandshakeData { get; }
|
||||
|
||||
static HandshakeProtocol()
|
||||
{
|
||||
var memoryBufferWriter = MemoryBufferWriter.Get();
|
||||
try
|
||||
{
|
||||
WriteResponseMessage(HandshakeResponseMessage.Empty, memoryBufferWriter);
|
||||
SuccessHandshakeData = memoryBufferWriter.ToArray();
|
||||
}
|
||||
finally
|
||||
{
|
||||
MemoryBufferWriter.Return(memoryBufferWriter);
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteRequestMessage(HandshakeRequestMessage requestMessage, IBufferWriter<byte> output)
|
||||
{
|
||||
var textWriter = Utf8BufferTextWriter.Get(output);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// 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 Microsoft.AspNetCore.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
|
||||
{
|
||||
public static class HubProtocolExtensions
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@
|
|||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Common\JsonUtils.cs" Link="Internal\JsonUtils.cs" />
|
||||
<Compile Include="..\Common\MemoryBufferWriter.cs" Link="Internal\MemoryBufferWriter.cs" />
|
||||
<Compile Include="..\Common\Utf8BufferTextReader.cs" Link="Internal\Utf8BufferTextReader.cs" />
|
||||
<Compile Include="..\Common\Utf8BufferTextWriter.cs" Link="Internal\Utf8BufferTextWriter.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ using System.Threading.Tasks;
|
|||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Connections.Features;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Internal;
|
||||
using Microsoft.AspNetCore.SignalR.Core;
|
||||
using Microsoft.AspNetCore.SignalR.Internal;
|
||||
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
||||
|
|
@ -24,7 +25,6 @@ namespace Microsoft.AspNetCore.SignalR
|
|||
public class HubConnectionContext
|
||||
{
|
||||
private static readonly Action<object> _abortedCallback = AbortConnection;
|
||||
private static readonly byte[] _successHandshakeResponseData;
|
||||
|
||||
private readonly ConnectionContext _connectionContext;
|
||||
private readonly ILogger _logger;
|
||||
|
|
@ -36,20 +36,6 @@ namespace Microsoft.AspNetCore.SignalR
|
|||
private long _lastSendTimestamp = Stopwatch.GetTimestamp();
|
||||
private byte[] _cachedPingMessage;
|
||||
|
||||
static HubConnectionContext()
|
||||
{
|
||||
var memoryBufferWriter = MemoryBufferWriter.Get();
|
||||
try
|
||||
{
|
||||
HandshakeProtocol.WriteResponseMessage(HandshakeResponseMessage.Empty, memoryBufferWriter);
|
||||
_successHandshakeResponseData = memoryBufferWriter.ToArray();
|
||||
}
|
||||
finally
|
||||
{
|
||||
MemoryBufferWriter.Return(memoryBufferWriter);
|
||||
}
|
||||
}
|
||||
|
||||
public HubConnectionContext(ConnectionContext connectionContext, TimeSpan keepAliveInterval, ILoggerFactory loggerFactory)
|
||||
{
|
||||
_connectionContext = connectionContext;
|
||||
|
|
@ -264,7 +250,7 @@ namespace Microsoft.AspNetCore.SignalR
|
|||
if (message == HandshakeResponseMessage.Empty)
|
||||
{
|
||||
// success response is always an empty object so send cached data
|
||||
_connectionContext.Transport.Output.Write(_successHandshakeResponseData);
|
||||
_connectionContext.Transport.Output.Write(HandshakeProtocol.SuccessHandshakeData.Span);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,10 +4,13 @@
|
|||
<Description>Implements the SignalR Hub Protocol over JSON.</Description>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<RootNamespace>Microsoft.AspNetCore.SignalR</RootNamespace>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Common\JsonUtils.cs" Link="Internal\JsonUtils.cs" />
|
||||
<Compile Include="..\Common\Utf8BufferTextReader.cs" Link="Utf8BufferTextReader.cs" />
|
||||
<Compile Include="..\Common\Utf8BufferTextWriter.cs" Link="Utf8BufferTextWriter.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@
|
|||
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\..\src\Common\MemoryBufferWriter.cs" Link="MemoryBufferWriter.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\xunit.runner.json" Link="xunit.runner.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.SignalR.Internal;
|
||||
using Microsoft.AspNetCore.Internal;
|
||||
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
|
||||
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
||||
using Newtonsoft.Json;
|
||||
|
|
@ -75,17 +75,21 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
|||
{
|
||||
var s = await ReadSentTextMessageAsync();
|
||||
|
||||
byte[] response;
|
||||
|
||||
var output = MemoryBufferWriter.Get();
|
||||
try
|
||||
{
|
||||
HandshakeProtocol.WriteResponseMessage(HandshakeResponseMessage.Empty, output);
|
||||
await Application.Output.WriteAsync(output.ToArray());
|
||||
response = output.ToArray();
|
||||
}
|
||||
finally
|
||||
{
|
||||
MemoryBufferWriter.Return(output);
|
||||
}
|
||||
|
||||
await Application.Output.WriteAsync(response);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Internal;
|
||||
using Microsoft.AspNetCore.SignalR.Internal;
|
||||
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
|
||||
using Xunit;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Internal;
|
||||
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
|
||||
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
|
@ -16,7 +17,6 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
|
||||
{
|
||||
using Microsoft.AspNetCore.SignalR.Internal;
|
||||
using static HubMessageHelpers;
|
||||
|
||||
public class JsonHubProtocolTests
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.SignalR.Internal;
|
||||
using Microsoft.AspNetCore.Internal;
|
||||
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
|
||||
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
||||
using MsgPack;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Internal;
|
||||
using Microsoft.AspNetCore.SignalR.Internal;
|
||||
using Xunit;
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Connections.Features;
|
||||
using Microsoft.AspNetCore.Internal;
|
||||
using Microsoft.AspNetCore.SignalR.Internal;
|
||||
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
||||
|
||||
|
|
@ -16,7 +17,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
|||
{
|
||||
public class TestClient : ITransferFormatFeature, IConnectionHeartbeatFeature, IDisposable
|
||||
{
|
||||
private object _heartbeatLock = new object();
|
||||
private readonly object _heartbeatLock = new object();
|
||||
private List<(Action<object> handler, object state)> _heartbeatHandlers;
|
||||
|
||||
private static int _id;
|
||||
|
|
|
|||
Loading…
Reference in New Issue