diff --git a/eng/Versions.props b/eng/Versions.props index e86eb1601c..aa382cd021 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -221,7 +221,7 @@ 4.2.1 4.2.1 3.8.0 - 0.1.22-pre2 + 0.1.22-pre3 3.0.0-preview3.4 3.0.0-preview3.4 3.0.0-preview3.4 diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/Program.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/Program.cs index 94905231a0..d1f9df7226 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/Program.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/Program.cs @@ -15,6 +15,8 @@ namespace GrpcService_CSharp CreateHostBuilder(args).Build().Run(); } + // Additional configuration is required to successfully run gRPC on macOS. + // For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682 public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/.template.config/template.json index 93dd531436..f919655bd8 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/.template.config/template.json @@ -6,6 +6,7 @@ "SPA" ], "groupIdentity": "Microsoft.DotNet.Web.Spa.ProjectTemplates.Angular", + "precedence": "5000", "identity": "Microsoft.DotNet.Web.Spa.ProjectTemplates.Angular.CSharp.3.0", "name": "ASP.NET Core with Angular", "preferNameDirectory": true, diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/.template.config/template.json index 7eff01916f..3c4b69d0b9 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/.template.config/template.json @@ -6,6 +6,7 @@ "SPA" ], "groupIdentity": "Microsoft.DotNet.Web.Spa.ProjectTemplates.React", + "precedence": "5000", "identity": "Microsoft.DotNet.Web.Spa.ProjectTemplates.React.CSharp.3.0", "name": "ASP.NET Core with React.js", "preferNameDirectory": true, @@ -189,7 +190,6 @@ "binding": "HostIdentifier" } }, - "tags": { "language": "C#", "type": "project" diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/ReactRedux-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/ReactRedux-CSharp/.template.config/template.json index 660c888df4..264061a004 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/ReactRedux-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/ReactRedux-CSharp/.template.config/template.json @@ -6,6 +6,7 @@ "SPA" ], "groupIdentity": "Microsoft.DotNet.Web.Spa.ProjectTemplates.ReactRedux", + "precedence": "5000", "identity": "Microsoft.DotNet.Web.Spa.ProjectTemplates.ReactRedux.CSharp.3.0", "name": "ASP.NET Core with React.js and Redux", "preferNameDirectory": true, diff --git a/src/SignalR/server/Core/ref/Microsoft.AspNetCore.SignalR.Core.netcoreapp3.0.cs b/src/SignalR/server/Core/ref/Microsoft.AspNetCore.SignalR.Core.netcoreapp3.0.cs index 6e2f3f1f71..5309dae32c 100644 --- a/src/SignalR/server/Core/ref/Microsoft.AspNetCore.SignalR.Core.netcoreapp3.0.cs +++ b/src/SignalR/server/Core/ref/Microsoft.AspNetCore.SignalR.Core.netcoreapp3.0.cs @@ -144,6 +144,7 @@ namespace Microsoft.AspNetCore.SignalR public HubConnectionContextOptions() { } public System.TimeSpan ClientTimeoutInterval { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public System.TimeSpan KeepAliveInterval { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public long? MaximumReceiveMessageSize { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public int StreamBufferCapacity { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } } public partial class HubConnectionHandler : Microsoft.AspNetCore.Connections.ConnectionHandler where THub : Microsoft.AspNetCore.SignalR.Hub diff --git a/src/SignalR/server/Core/src/HubConnectionContext.cs b/src/SignalR/server/Core/src/HubConnectionContext.cs index ba3a8a0201..cfc0137b36 100644 --- a/src/SignalR/server/Core/src/HubConnectionContext.cs +++ b/src/SignalR/server/Core/src/HubConnectionContext.cs @@ -43,6 +43,7 @@ namespace Microsoft.AspNetCore.SignalR private bool _clientTimeoutActive; private bool _connectionAborted; private int _streamBufferCapacity; + private long? _maxMessageSize; /// /// Initializes a new instance of the class. @@ -55,6 +56,7 @@ namespace Microsoft.AspNetCore.SignalR _keepAliveInterval = contextOptions.KeepAliveInterval.Ticks; _clientTimeoutInterval = contextOptions.ClientTimeoutInterval.Ticks; _streamBufferCapacity = contextOptions.StreamBufferCapacity; + _maxMessageSize = contextOptions.MaximumReceiveMessageSize; _connectionContext = connectionContext; _logger = loggerFactory.CreateLogger(); @@ -406,10 +408,20 @@ namespace Microsoft.AspNetCore.SignalR if (!buffer.IsEmpty) { - if (HandshakeProtocol.TryParseRequestMessage(ref buffer, out var handshakeRequestMessage)) + var segment = buffer; + var overLength = false; + + if (_maxMessageSize != null && buffer.Length > _maxMessageSize.Value) + { + // We give the parser a sliding window of the default message size + segment = segment.Slice(segment.Start, _maxMessageSize.Value); + overLength = true; + } + + if (HandshakeProtocol.TryParseRequestMessage(ref segment, out var handshakeRequestMessage)) { // We parsed the handshake - consumed = buffer.Start; + consumed = segment.Start; examined = consumed; Protocol = protocolResolver.GetProtocol(handshakeRequestMessage.Protocol, supportedProtocols); @@ -461,6 +473,12 @@ namespace Microsoft.AspNetCore.SignalR await WriteHandshakeResponseAsync(HandshakeResponseMessage.Empty); return true; } + else if (overLength) + { + Log.HandshakeSizeLimitExceeded(_logger, _maxMessageSize.Value); + await WriteHandshakeResponseAsync(new HandshakeResponseMessage("Handshake was canceled.")); + return false; + } } if (result.IsCompleted) @@ -619,6 +637,9 @@ namespace Microsoft.AspNetCore.SignalR private static readonly Action _clientTimeout = LoggerMessage.Define(LogLevel.Debug, new EventId(9, "ClientTimeout"), "Client timeout ({ClientTimeout}ms) elapsed without receiving a message from the client. Closing connection."); + private static readonly Action _handshakeSizeLimitExceeded = + LoggerMessage.Define(LogLevel.Debug, new EventId(10, "HandshakeSizeLimitExceeded"), "The maximum message size of {MaxMessageSize}B was exceeded while parsing the Handshake. The message size can be configured in AddHubOptions."); + public static void HandshakeComplete(ILogger logger, string hubProtocol) { _handshakeComplete(logger, hubProtocol, null); @@ -663,6 +684,11 @@ namespace Microsoft.AspNetCore.SignalR { _clientTimeout(logger, (int)timeout.TotalMilliseconds, null); } + + public static void HandshakeSizeLimitExceeded(ILogger logger, long maxMessageSize) + { + _handshakeSizeLimitExceeded(logger, maxMessageSize, null); + } } } } diff --git a/src/SignalR/server/Core/src/HubConnectionContextOptions.cs b/src/SignalR/server/Core/src/HubConnectionContextOptions.cs index a38096cbef..c95c58afe6 100644 --- a/src/SignalR/server/Core/src/HubConnectionContextOptions.cs +++ b/src/SignalR/server/Core/src/HubConnectionContextOptions.cs @@ -24,5 +24,10 @@ namespace Microsoft.AspNetCore.SignalR /// Gets or sets the max buffer size for client upload streams. /// public int StreamBufferCapacity { get; set; } + + /// + /// Gets or sets the maximum message size the client can send. + /// + public long? MaximumReceiveMessageSize { get; set; } } } diff --git a/src/SignalR/server/Core/src/HubConnectionHandler.cs b/src/SignalR/server/Core/src/HubConnectionHandler.cs index de6a3dd3c0..270eb44c0c 100644 --- a/src/SignalR/server/Core/src/HubConnectionHandler.cs +++ b/src/SignalR/server/Core/src/HubConnectionHandler.cs @@ -89,6 +89,7 @@ namespace Microsoft.AspNetCore.SignalR KeepAliveInterval = _hubOptions.KeepAliveInterval ?? _globalHubOptions.KeepAliveInterval ?? HubOptionsSetup.DefaultKeepAliveInterval, ClientTimeoutInterval = _hubOptions.ClientTimeoutInterval ?? _globalHubOptions.ClientTimeoutInterval ?? HubOptionsSetup.DefaultClientTimeoutInterval, StreamBufferCapacity = _hubOptions.StreamBufferCapacity ?? _globalHubOptions.StreamBufferCapacity ?? HubOptionsSetup.DefaultStreamBufferCapacity, + MaximumReceiveMessageSize = _maximumMessageSize, }; Log.ConnectedStarting(_logger); diff --git a/src/SignalR/server/SignalR/test/HubConnectionHandlerTests.cs b/src/SignalR/server/SignalR/test/HubConnectionHandlerTests.cs index d4188e3918..f2eb574c9d 100644 --- a/src/SignalR/server/SignalR/test/HubConnectionHandlerTests.cs +++ b/src/SignalR/server/SignalR/test/HubConnectionHandlerTests.cs @@ -292,6 +292,40 @@ namespace Microsoft.AspNetCore.SignalR.Tests } } + [Fact] + public async Task ConnectionClosedWhenHandshakeLargerThanMaxMessageSize() + { + using (StartVerifiableLog()) + { + var connectionHandler = HubConnectionHandlerTestUtils.GetHubConnectionHandler(typeof(HubT), loggerFactory: LoggerFactory, + builder => + { + builder.AddSignalR(o => + { + o.MaximumReceiveMessageSize = 1; + }); + }); + + using (var client = new TestClient()) + { + client.SupportedFormats = TransferFormat.Text; + + var connectionHandlerTask = await client.ConnectAsync(connectionHandler, + sendHandshakeRequestMessage: true, + expectedHandshakeResponseMessage: false); + + var message = await client.ReadAsync(isHandshake: true).OrTimeout(); + + Assert.Equal("Handshake was canceled.", ((HandshakeResponseMessage)message).Error); + + // Connection closes + await connectionHandlerTask.OrTimeout(); + + client.Dispose(); + } + } + } + [Fact] public async Task SendingHandshakeRequestInChunksWorks() {